@foisit/react-wrapper 2.1.2 → 2.4.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/index.mjs CHANGED
@@ -1,253 +1,364 @@
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!" }) });
1
+ import { jsx as k } from "react/jsx-runtime";
2
+ import { createContext as F, useState as R, useEffect as P, useContext as N } from "react";
3
+ const W = {};
4
+ function Q() {
5
+ return /* @__PURE__ */ k("div", { className: W.container, children: /* @__PURE__ */ k("h1", { children: "Welcome to ReactWrapper!" }) });
6
6
  }
7
- class L {
8
- constructor() {
9
- this.endpoint = "https://foisit-ninja.netlify.app/.netlify/functions/intent";
7
+ class M {
8
+ constructor(t) {
9
+ this.endpoint = t || "https://foisit-ninja.netlify.app/.netlify/functions/intent";
10
10
  }
11
- /**
12
- * Determine the best matching command by calling the internalized proxy
13
- */
14
- async determineIntent(e, t, i) {
11
+ async determineIntent(t, e, i) {
15
12
  try {
16
- const s = await fetch(this.endpoint, {
13
+ const s = {
14
+ userInput: t,
15
+ commands: e.map((r) => ({
16
+ id: r.id,
17
+ command: r.command,
18
+ description: r.description,
19
+ parameters: r.parameters
20
+ // Send param schemas to AI
21
+ })),
22
+ context: i
23
+ }, n = await fetch(this.endpoint, {
17
24
  method: "POST",
18
25
  headers: {
19
26
  "Content-Type": "application/json"
20
27
  },
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
- })
28
+ body: JSON.stringify(s)
32
29
  });
33
- if (!s.ok)
34
- throw new Error("Proxy API request failed");
35
- return await s.json();
30
+ if (!n.ok)
31
+ throw new Error(`Proxy API Error: ${n.statusText}`);
32
+ return await n.json();
36
33
  } catch (s) {
37
- return console.error("Foisit AI Error:", s), { type: "unknown" };
34
+ return console.error("OpenAIService Error:", s), { type: "unknown" };
38
35
  }
39
36
  }
40
37
  }
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();
38
+ class B {
39
+ constructor(t = !0) {
40
+ if (this.commands = /* @__PURE__ */ new Map(), this.openAIService = null, this.context = null, this.pendingConfirmation = null, this.enableSmartIntent = !0, this.selectOptionsCache = /* @__PURE__ */ new Map(), typeof t == "boolean") {
41
+ this.enableSmartIntent = t, this.enableSmartIntent && (this.openAIService = new M());
42
+ return;
43
+ }
44
+ this.enableSmartIntent = t.enableSmartIntent ?? !0, this.enableSmartIntent && (this.openAIService = new M(t.intentEndpoint));
44
45
  }
45
- /**
46
- * Add a new command (supports simple string or rich object)
47
- */
48
- addCommand(e, t) {
46
+ /** Add a new command (string or object) */
47
+ addCommand(t, e) {
49
48
  let i;
50
- if (typeof e == "string") {
51
- if (!t)
52
- throw new Error("Action is required when passing a command string.");
49
+ if (typeof t == "string") {
50
+ if (!e)
51
+ throw new Error("Action required when adding command by string.");
53
52
  i = {
54
- command: e,
55
- action: t,
56
- id: `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
57
- // Auto-gen ID
53
+ id: t.toLowerCase().replace(/\s+/g, "_"),
54
+ command: t.toLowerCase(),
55
+ action: e
58
56
  };
59
57
  } 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);
58
+ i = { ...t }, i.id || (i.id = i.command.toLowerCase().replace(/\s+/g, "_"));
59
+ this.commands.set(i.command.toLowerCase(), i), i.id && (i.id, i.command);
60
+ }
61
+ /** Remove an existing command */
62
+ removeCommand(t) {
63
+ this.commands.delete(t.toLowerCase());
64
+ }
65
+ /** Execute a command by matching input */
66
+ async executeCommand(t) {
67
+ if (typeof t == "object" && t !== null) {
68
+ if (this.isStructured(t)) {
69
+ const c = String(t.commandId), f = t.params ?? {}, a = this.getCommandById(c);
70
+ if (!a) return { message: "That command is not available.", type: "error" };
71
+ const p = this.sanitizeParamsForCommand(a, f), m = (a.parameters ?? []).filter((C) => C.required).filter((C) => p[C.name] == null || p[C.name] === "");
72
+ return m.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(a), params: p }, {
73
+ message: `Please provide the required details for "${a.command}".`,
74
+ type: "form",
75
+ fields: m
76
+ }) : a.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(a), params: p }, this.buildConfirmResponse(a)) : this.safeRunAction(a, p);
82
77
  }
78
+ if (!this.context)
79
+ return { message: "Session expired or invalid context.", type: "error" };
80
+ const n = this.getCommandById(this.context.commandId);
81
+ if (!n)
82
+ return this.context = null, { message: "Session expired or invalid context.", type: "error" };
83
+ if (Array.isArray(t))
84
+ return { message: "Invalid form payload.", type: "error" };
85
+ const o = {
86
+ ...this.context.params,
87
+ ...t
88
+ };
89
+ if (n.critical)
90
+ return this.context = null, this.pendingConfirmation = {
91
+ commandId: this.getCommandIdentifier(n),
92
+ params: o
93
+ }, this.buildConfirmResponse(n);
94
+ const r = await this.safeRunAction(n, o);
95
+ return this.context = null, this.normalizeResponse(r);
83
96
  }
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}?`,
97
+ const e = t.trim().toLowerCase();
98
+ if (this.pendingConfirmation) {
99
+ const n = e;
100
+ if (["yes", "y", "confirm", "ok", "okay"].includes(n)) {
101
+ const { commandId: o, params: r } = this.pendingConfirmation;
102
+ this.pendingConfirmation = null;
103
+ const c = this.getCommandById(o);
104
+ return c ? this.safeRunAction(c, r) : { message: "That action is no longer available.", type: "error" };
105
+ }
106
+ return ["no", "n", "cancel", "stop"].includes(n) ? (this.pendingConfirmation = null, { message: "✅ Cancelled.", type: "success" }) : {
107
+ message: "Please confirm: Yes or No.",
108
+ type: "confirm",
92
109
  options: [
93
110
  { label: "Yes", value: "yes" },
94
111
  { label: "No", value: "no" }
95
112
  ]
96
113
  };
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
114
  }
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
115
+ const i = this.commands.get(e);
116
+ if (i) {
117
+ const n = i, o = (n.parameters ?? []).filter((r) => r.required);
118
+ return o.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
119
+ message: `Please provide the required details for "${n.command}".`,
120
+ type: "form",
121
+ fields: o
122
+ }) : n.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(n), params: {} }, this.buildConfirmResponse(n)) : this.safeRunAction(n, {});
123
+ }
124
+ const s = await this.tryDeterministicMatch(e);
125
+ if (s) return s;
126
+ if (this.enableSmartIntent && this.openAIService) {
127
+ const n = await this.getCommandsForAI(), o = await this.openAIService.determineIntent(
128
+ e,
129
+ n,
130
+ this.context
145
131
  );
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 || {}
132
+ return this.handleAIResult(o);
133
+ }
134
+ return this.enableSmartIntent ? this.listAllCommands() : { message: "I'm not sure what you mean.", type: "error" };
135
+ }
136
+ async handleAIResult(t) {
137
+ if (t.type === "match" && t.match) {
138
+ const e = this.getCommandById(t.match);
139
+ if (!e)
140
+ return { message: "I'm not sure what you mean.", type: "error" };
141
+ const i = t.params ?? {}, s = this.sanitizeParamsForCommand(e, i), n = e.allowAiParamExtraction === !1 ? {} : s, r = (e.parameters ?? []).filter((f) => f.required).filter((f) => n[f.name] == null || n[f.name] === "");
142
+ if (t.incomplete || r.length > 0) {
143
+ if (this.context = { commandId: this.getCommandIdentifier(e), params: n }, !(e.collectRequiredViaForm !== !1) && this.shouldAskSingleQuestion(r)) {
144
+ const p = r.map((g) => g.name).join(" and ");
145
+ return {
146
+ message: t.message || `Please provide ${p}.`,
147
+ type: "question"
152
148
  };
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
149
  }
200
- } else if (o.type === "ambiguous" && o.options)
201
150
  return {
202
- type: "ambiguous",
203
- message: "Did you mean...",
204
- options: o.options.map((a) => ({
205
- label: a.label,
206
- value: a.label
207
- }))
151
+ message: t.message || `Please fill in the missing details for "${e.command}".`,
152
+ type: "form",
153
+ fields: r
208
154
  };
155
+ }
156
+ if (e.critical)
157
+ return this.pendingConfirmation = {
158
+ commandId: this.getCommandIdentifier(e),
159
+ params: n
160
+ }, this.buildConfirmResponse(e);
161
+ const c = await e.action(n);
162
+ return this.normalizeResponse(c);
209
163
  }
210
- return {
164
+ return t.type === "ambiguous" && t.options && t.options.length ? {
165
+ message: t.message || "Did you mean one of these?",
211
166
  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
167
+ options: t.options.map((e) => ({
168
+ label: e.label,
169
+ value: e.commandId ?? e.label,
170
+ commandId: e.commandId
216
171
  }))
172
+ } : this.listAllCommands();
173
+ }
174
+ sanitizeParamsForCommand(t, e) {
175
+ const i = { ...e ?? {} };
176
+ for (const s of t.parameters ?? []) {
177
+ const n = i[s.name];
178
+ if (s.type === "string") {
179
+ if (typeof n != "string") {
180
+ delete i[s.name];
181
+ continue;
182
+ }
183
+ const o = n.trim();
184
+ if (!o) {
185
+ delete i[s.name];
186
+ continue;
187
+ }
188
+ i[s.name] = o;
189
+ }
190
+ if (s.type === "number") {
191
+ const o = typeof n == "number" ? n : Number(n == null ? void 0 : n.toString().trim());
192
+ if (Number.isNaN(o)) {
193
+ delete i[s.name];
194
+ continue;
195
+ }
196
+ if (typeof s.min == "number" && o < s.min) {
197
+ delete i[s.name];
198
+ continue;
199
+ }
200
+ if (typeof s.max == "number" && o > s.max) {
201
+ delete i[s.name];
202
+ continue;
203
+ }
204
+ i[s.name] = o;
205
+ }
206
+ if (s.type === "date") {
207
+ const o = i[s.name];
208
+ if (typeof o == "string") {
209
+ const r = o.trim();
210
+ this.isIsoDateString(r) ? i[s.name] = r : delete i[s.name];
211
+ } else
212
+ delete i[s.name];
213
+ }
214
+ if (s.type === "select") {
215
+ const o = typeof n == "string" ? n : n == null ? void 0 : n.toString();
216
+ if (!o) {
217
+ delete i[s.name];
218
+ continue;
219
+ }
220
+ if (Array.isArray(s.options) && s.options.length > 0 && !s.options.some((c) => String(c.value) === String(o))) {
221
+ delete i[s.name];
222
+ continue;
223
+ }
224
+ i[s.name] = o;
225
+ }
226
+ if (s.type === "file") {
227
+ const o = i[s.name], r = o && typeof o == "object" && typeof o.name == "string" && typeof o.size == "number", c = typeof o == "string" && /^data:[^;]+;base64,/.test(o);
228
+ (s.delivery ?? "file") === "base64" ? !c && !r && delete i[s.name] : r || delete i[s.name];
229
+ }
230
+ }
231
+ return i;
232
+ }
233
+ isIsoDateString(t) {
234
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(t)) return !1;
235
+ const e = /* @__PURE__ */ new Date(`${t}T00:00:00Z`);
236
+ return !Number.isNaN(e.getTime());
237
+ }
238
+ shouldAskSingleQuestion(t) {
239
+ if (t.length !== 1) return !1;
240
+ const e = t[0].type;
241
+ return e === "string" || e === "number" || e === "date";
242
+ }
243
+ buildConfirmResponse(t) {
244
+ return {
245
+ message: `⚠️ Are you sure you want to run "${t.command}"?`,
246
+ type: "confirm",
247
+ options: [
248
+ { label: "Yes", value: "yes" },
249
+ { label: "No", value: "no" }
250
+ ]
217
251
  };
218
252
  }
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) {
253
+ async tryDeterministicMatch(t) {
254
+ const e = [];
255
+ for (const r of this.commands.values()) {
256
+ let c = 0;
257
+ const f = r.command.toLowerCase();
258
+ t.includes(f) && (c += 5);
259
+ const a = r.keywords ?? [];
260
+ for (const p of a) {
261
+ const g = p.toLowerCase().trim();
262
+ g && (t === g ? c += 4 : t.includes(g) && (c += 3));
263
+ }
264
+ c > 0 && e.push({ cmd: r, score: c });
265
+ }
266
+ if (e.length === 0) return null;
267
+ e.sort((r, c) => c.score - r.score);
268
+ const i = e[0].score, s = e.filter((r) => r.score === i).slice(0, 3);
269
+ if (s.length > 1)
228
270
  return {
229
- type: "error",
230
- message: i.message || "An error occurred during execution."
271
+ message: "I think you mean one of these. Which one should I run?",
272
+ type: "ambiguous",
273
+ options: s.map((r) => ({
274
+ label: r.cmd.command,
275
+ value: r.cmd.command,
276
+ commandId: r.cmd.id
277
+ }))
231
278
  };
279
+ const n = s[0].cmd, o = (n.parameters ?? []).filter((r) => r.required);
280
+ return o.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
281
+ message: `Please provide the required details for "${n.command}".`,
282
+ type: "form",
283
+ fields: o
284
+ }) : n.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(n), params: {} }, this.buildConfirmResponse(n)) : this.safeRunAction(n, {});
285
+ }
286
+ async safeRunAction(t, e) {
287
+ try {
288
+ const i = await t.action(e ?? {});
289
+ return this.normalizeResponse(i);
290
+ } catch {
291
+ return { message: "Something went wrong while running that command.", type: "error" };
232
292
  }
233
293
  }
294
+ async getCommandsForAI() {
295
+ const t = Array.from(this.commands.values()).map((e) => ({
296
+ ...e,
297
+ parameters: e.parameters ? e.parameters.map((i) => ({ ...i })) : void 0
298
+ }));
299
+ return await Promise.all(
300
+ t.map(async (e) => {
301
+ e.parameters && await Promise.all(
302
+ e.parameters.map(async (i) => {
303
+ if (i.type !== "select" || !i.getOptions || i.options && i.options.length) return;
304
+ const s = `${e.id ?? e.command}:${i.name}`, n = this.selectOptionsCache.get(s), o = Date.now();
305
+ if (n && o - n.ts < 6e4) {
306
+ i.options = n.options;
307
+ return;
308
+ }
309
+ try {
310
+ const r = await i.getOptions();
311
+ this.selectOptionsCache.set(s, { options: r, ts: o }), i.options = r;
312
+ } catch {
313
+ }
314
+ })
315
+ );
316
+ })
317
+ ), t;
318
+ }
319
+ getCommandById(t) {
320
+ for (const e of this.commands.values())
321
+ if (e.id === t) return e;
322
+ }
323
+ listAllCommands() {
324
+ return {
325
+ message: "Here are the available commands:",
326
+ type: "ambiguous",
327
+ options: Array.from(this.commands.values()).map((e) => ({
328
+ label: e.command,
329
+ value: e.id ?? e.command,
330
+ commandId: e.id ?? e.command
331
+ }))
332
+ };
333
+ }
334
+ normalizeResponse(t) {
335
+ return typeof t == "string" ? { message: t, type: "success" } : t && typeof t == "object" ? t : { message: "Done", type: "success" };
336
+ }
337
+ isStructured(t) {
338
+ return typeof t.commandId == "string";
339
+ }
340
+ getCommandIdentifier(t) {
341
+ return t.id || (t.id = t.command.toLowerCase().replace(/\s+/g, "_")), t.id;
342
+ }
234
343
  /** List all registered commands */
235
344
  getCommands() {
236
345
  return Array.from(this.commands.keys());
237
346
  }
238
347
  }
239
- class k {
348
+ class q {
240
349
  constructor() {
241
350
  this.synth = window.speechSynthesis;
242
351
  }
243
- speak(e, t) {
352
+ speak(t, e) {
244
353
  if (!this.synth) {
245
354
  console.error("SpeechSynthesis API is not supported in this browser.");
246
355
  return;
247
356
  }
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.");
357
+ const i = new SpeechSynthesisUtterance(t);
358
+ e && (i.pitch = e.pitch || 1, i.rate = e.rate || 1, i.volume = e.volume || 1), i.onstart = () => {
359
+ window.dispatchEvent(new CustomEvent("foisit:tts-start"));
360
+ }, i.onend = () => {
361
+ console.log("Speech finished."), window.dispatchEvent(new CustomEvent("foisit:tts-end"));
251
362
  }, i.onerror = (s) => {
252
363
  console.error("Error during speech synthesis:", s.error);
253
364
  }, this.synth.speak(i);
@@ -256,53 +367,191 @@ class k {
256
367
  this.synth && this.synth.cancel();
257
368
  }
258
369
  }
259
- class P {
370
+ class z {
260
371
  constructor() {
261
372
  this.fallbackMessage = "Sorry, I didn’t understand that.";
262
373
  }
263
- setFallbackMessage(e) {
264
- this.fallbackMessage = e;
374
+ setFallbackMessage(t) {
375
+ this.fallbackMessage = t;
376
+ }
377
+ handleFallback(t) {
378
+ t && console.log(`Fallback triggered for: "${t}"`), console.log(this.fallbackMessage), new q().speak(this.fallbackMessage);
265
379
  }
266
- handleFallback(e) {
267
- console.log(this.fallbackMessage), new k().speak(this.fallbackMessage);
380
+ getFallbackMessage() {
381
+ return this.fallbackMessage;
268
382
  }
269
383
  }
270
- class T {
271
- constructor(e = "en-US") {
272
- this.isListening = !1, this.isStoppedSpeechRecog = !1, this.restartAllowed = !0;
384
+ const T = () => {
385
+ const d = window;
386
+ return d.SpeechRecognition ?? d.webkitSpeechRecognition ?? null;
387
+ };
388
+ class O {
389
+ constructor(t = "en-US", e = {}) {
390
+ this.recognition = null, this.isListening = !1, this.engineActive = !1, this.intentionallyStopped = !1, this.restartAllowed = !0, this.lastStart = 0, this.backoffMs = 250, this.destroyed = !1, this.resultCallback = null, this.ttsSpeaking = !1, this.debugEnabled = !0, this.restartTimer = null, this.prewarmed = !1, this.hadResultThisSession = !1, this.onTTSStart = () => {
391
+ var s;
392
+ this.ttsSpeaking = !0;
393
+ try {
394
+ (s = this.recognition) == null || s.stop();
395
+ } catch {
396
+ }
397
+ this.isListening && this.emitStatus("speaking");
398
+ }, this.onTTSEnd = () => {
399
+ this.ttsSpeaking = !1, this.isListening && this.restartAllowed ? this.safeRestart() : this.emitStatus(this.isListening ? "listening" : "idle");
400
+ };
401
+ const i = T();
402
+ if (i) {
403
+ this.recognition = new i(), this.recognition.lang = t, this.recognition.interimResults = e.interimResults ?? !0, this.recognition.continuous = e.continuous ?? !0, this.recognition.onresult = (n) => this.handleResult(n, e), this.recognition.onend = () => this.handleEnd(), this.recognition.onstart = () => {
404
+ this.log("recognition onstart"), this.engineActive = !0, this.hadResultThisSession = !1, this.restartTimer && (clearTimeout(this.restartTimer), this.restartTimer = null), this.backoffMs = 250, this.isListening && !this.ttsSpeaking && this.emitStatus("listening");
405
+ };
406
+ const s = this.recognition;
407
+ s.onaudiostart = () => this.log("onaudiostart"), s.onsoundstart = () => this.log("onsoundstart"), s.onspeechstart = () => this.log("onspeechstart"), s.onspeechend = () => this.log("onspeechend"), s.onsoundend = () => this.log("onsoundend"), s.onaudioend = () => this.log("onaudioend"), this.recognition.onerror = (n) => this.handleError(n);
408
+ } else
409
+ this.recognition = null, this.emitStatus("unsupported");
410
+ window.addEventListener("foisit:tts-start", this.onTTSStart), window.addEventListener("foisit:tts-end", this.onTTSEnd), this.visibilityHandler = () => {
411
+ var s;
412
+ if (document.hidden) {
413
+ try {
414
+ (s = this.recognition) == null || s.stop();
415
+ } catch {
416
+ }
417
+ this.emitStatus(this.ttsSpeaking ? "speaking" : "idle");
418
+ } else this.isListening && !this.ttsSpeaking && this.safeRestart();
419
+ }, document.addEventListener("visibilitychange", this.visibilityHandler);
420
+ }
421
+ // Debug logger helpers
422
+ log(t) {
423
+ this.debugEnabled && t && console.log("[VoiceProcessor]", t);
424
+ }
425
+ warn(t) {
426
+ this.debugEnabled && t && console.warn("[VoiceProcessor]", t);
427
+ }
428
+ error(t) {
429
+ this.debugEnabled && t && console.error("[VoiceProcessor]", t);
430
+ }
431
+ /** Check if SpeechRecognition is available */
432
+ isSupported() {
433
+ return T() !== null;
434
+ }
435
+ /** Allow consumers (wrappers) to observe status changes */
436
+ onStatusChange(t) {
437
+ this.statusCallback = t;
273
438
  }
274
439
  /** Start listening for speech input */
275
- startListening(e) {
276
- console.warn("VoiceProcessor: Voice interaction is currently DISABLED.");
440
+ startListening(t) {
441
+ if (!this.isSupported() || !this.recognition) {
442
+ this.warn("VoiceProcessor: SpeechRecognition is not supported in this browser."), this.emitStatus("unsupported");
443
+ return;
444
+ }
445
+ if (this.isListening) {
446
+ this.warn("VoiceProcessor: Already listening."), this.resultCallback = t;
447
+ return;
448
+ }
449
+ this.resultCallback = t, this.intentionallyStopped = !1, this.restartAllowed = !0, this.isListening = !0, this.emitStatus("listening"), this.prewarmAudio().finally(() => {
450
+ this.safeRestart();
451
+ });
277
452
  }
278
453
  /** Stop listening for speech input */
279
454
  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
- }
455
+ var t;
456
+ this.intentionallyStopped = !0, this.restartAllowed = !1, this.isListening = !1, this.emitStatus(this.ttsSpeaking ? "speaking" : "idle");
284
457
  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);
458
+ (t = this.recognition) == null || t.stop();
459
+ } catch {
288
460
  }
289
461
  }
462
+ /** Clean up listeners */
463
+ destroy() {
464
+ this.destroyed = !0, this.stopListening(), this.resultCallback = null, window.removeEventListener("foisit:tts-start", this.onTTSStart), window.removeEventListener("foisit:tts-end", this.onTTSEnd), this.visibilityHandler && (document.removeEventListener("visibilitychange", this.visibilityHandler), this.visibilityHandler = void 0);
465
+ }
290
466
  /** Handle recognized speech results */
291
- handleResult(e, t) {
467
+ handleResult(t, e) {
468
+ var s, n;
469
+ if (!this.resultCallback) return;
470
+ const i = e.confidenceThreshold ?? 0.6;
471
+ for (let o = t.resultIndex; o < t.results.length; o++) {
472
+ const r = t.results[o], c = r && r[0], f = ((n = (s = c == null ? void 0 : c.transcript) == null ? void 0 : s.trim) == null ? void 0 : n.call(s)) || "", a = (c == null ? void 0 : c.confidence) ?? 0;
473
+ if (f && !(!r.isFinal && e.interimResults === !1) && !(r.isFinal && a < i))
474
+ try {
475
+ this.hadResultThisSession = !0, this.resultCallback(f, !!r.isFinal);
476
+ } catch {
477
+ this.error("VoiceProcessor: result callback error");
478
+ }
479
+ }
292
480
  }
293
481
  /** Handle session end */
294
482
  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));
483
+ if (this.log("recognition onend"), this.engineActive = !1, this.destroyed || this.intentionallyStopped || !this.restartAllowed || this.ttsSpeaking) {
484
+ this.ttsSpeaking || (this.isListening = !1, this.emitStatus("idle"));
485
+ return;
486
+ }
487
+ this.isListening = !0, this.scheduleRestart();
299
488
  }
300
489
  /** 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;
490
+ handleError(t) {
491
+ const e = t == null ? void 0 : t.error;
492
+ if (this.warn(`Error occurred: ${e ?? "unknown"}`), e && ["not-allowed", "service-not-allowed", "bad-grammar", "language-not-supported"].includes(e)) {
493
+ this.intentionallyStopped = !0, this.restartAllowed = !1, this.isListening = !1, this.emitStatus("error", { error: e });
494
+ return;
495
+ }
496
+ this.scheduleRestart();
497
+ }
498
+ safeRestart() {
499
+ if (!this.recognition) return;
500
+ if (this.engineActive) {
501
+ this.log("safeRestart: engine already active, skipping start");
502
+ return;
503
+ }
504
+ const t = Date.now();
505
+ if (t - this.lastStart < 300) {
506
+ setTimeout(() => this.safeRestart(), 300);
507
+ return;
508
+ }
509
+ this.lastStart = t;
510
+ try {
511
+ this.log("calling recognition.start()"), this.recognition.start(), this.backoffMs = 250, this.isListening && !this.ttsSpeaking && this.emitStatus("listening");
512
+ } catch {
513
+ this.error("recognition.start() threw; scheduling restart"), this.scheduleRestart();
514
+ }
515
+ }
516
+ scheduleRestart() {
517
+ if (this.destroyed || this.intentionallyStopped || !this.restartAllowed || this.ttsSpeaking) return;
518
+ if (this.engineActive) {
519
+ this.log("scheduleRestart: engine active, not scheduling");
520
+ return;
521
+ }
522
+ const t = Math.min(this.backoffMs, 2e3);
523
+ if (this.log(`scheduleRestart in ${t}ms`), this.restartTimer) {
524
+ this.log("scheduleRestart: restart already scheduled");
525
+ return;
526
+ }
527
+ this.restartTimer = setTimeout(() => {
528
+ this.restartTimer = null, !(this.destroyed || this.intentionallyStopped || !this.restartAllowed || this.ttsSpeaking) && this.safeRestart();
529
+ }, t), this.backoffMs = Math.min(this.backoffMs * 2, 2e3);
530
+ }
531
+ async prewarmAudio() {
532
+ if (!this.prewarmed)
533
+ try {
534
+ if (typeof navigator > "u" || !("mediaDevices" in navigator)) return;
535
+ const t = navigator.mediaDevices;
536
+ if (!(t != null && t.getUserMedia)) return;
537
+ this.log("prewarmAudio: requesting mic");
538
+ const e = await t.getUserMedia({ audio: !0 });
539
+ for (const i of e.getTracks()) i.stop();
540
+ this.prewarmed = !0, this.log("prewarmAudio: mic ready");
541
+ } catch {
542
+ this.warn("prewarmAudio: failed to get mic");
543
+ }
544
+ }
545
+ emitStatus(t, e) {
546
+ if (this.statusCallback)
547
+ try {
548
+ this.statusCallback(t, e);
549
+ } catch {
550
+ this.error("VoiceProcessor: status callback error");
551
+ }
303
552
  }
304
553
  }
305
- class B {
554
+ class U {
306
555
  constructor() {
307
556
  this.lastTap = 0;
308
557
  }
@@ -310,13 +559,16 @@ class B {
310
559
  * Sets up double-click and double-tap listeners
311
560
  * @param onDoubleClickOrTap Callback to execute when a double-click or double-tap is detected
312
561
  */
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
- });
562
+ setupDoubleTapListener(t) {
563
+ this.destroy(), this.dblClickListener = () => {
564
+ t();
565
+ }, document.addEventListener("dblclick", this.dblClickListener), this.touchEndListener = () => {
566
+ const e = (/* @__PURE__ */ new Date()).getTime(), i = e - this.lastTap;
567
+ i < 300 && i > 0 && t(), this.lastTap = e;
568
+ }, document.addEventListener("touchend", this.touchEndListener);
569
+ }
570
+ destroy() {
571
+ this.dblClickListener && document.removeEventListener("dblclick", this.dblClickListener), this.touchEndListener && document.removeEventListener("touchend", this.touchEndListener), this.dblClickListener = void 0, this.touchEndListener = void 0;
320
572
  }
321
573
  }
322
574
  function D() {
@@ -324,8 +576,8 @@ function D() {
324
576
  console.log("Styles already injected");
325
577
  return;
326
578
  }
327
- const e = document.createElement("style");
328
- e.id = "assistant-styles", e.innerHTML = `
579
+ const t = document.createElement("style");
580
+ t.id = "assistant-styles", t.innerHTML = `
329
581
  /* Rounded shape with gradient animation */
330
582
  .gradient-indicator {
331
583
  position: fixed;
@@ -358,727 +610,866 @@ function D() {
358
610
  border-radius: 50%;
359
611
  }
360
612
  }
361
- `, document.head.appendChild(e), console.log("Gradient styles injected");
613
+ `, document.head.appendChild(t), console.log("Gradient styles injected");
362
614
  }
363
- function R() {
615
+ function $() {
364
616
  if (document.querySelector("#gradient-indicator"))
365
617
  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");
618
+ const d = document.createElement("div");
619
+ d.id = "gradient-indicator", D(), d.classList.add("gradient-indicator"), document.body.appendChild(d), console.log("Gradient indicator added to the DOM");
368
620
  }
369
- function H() {
370
- const r = document.querySelector("#gradient-indicator");
371
- r && (r.remove(), console.log("Gradient indicator removed from the DOM"));
621
+ function j() {
622
+ const d = document.querySelector("#gradient-indicator");
623
+ d && (d.remove(), console.log("Gradient indicator removed from the DOM"));
372
624
  }
373
- class N {
625
+ class V {
374
626
  constructor() {
375
627
  this.state = "idle", this.subscribers = [];
376
628
  }
377
629
  getState() {
378
630
  return this.state;
379
631
  }
380
- setState(e) {
381
- this.state = e, this.notifySubscribers(), console.log("State updated:", e), e === "listening" ? R() : H();
632
+ setState(t) {
633
+ this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? $() : j();
382
634
  }
383
635
  // eslint-disable-next-line no-unused-vars
384
- subscribe(e) {
385
- this.subscribers.push(e);
636
+ subscribe(t) {
637
+ this.subscribers.push(t);
386
638
  }
387
639
  notifySubscribers() {
388
- this.subscribers.forEach((e) => e(this.state));
640
+ this.subscribers.forEach((t) => t(this.state));
389
641
  }
390
642
  }
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
- );
643
+ class G {
644
+ constructor(t) {
645
+ this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1, this.loadingEl = null, this.config = t, this.init();
396
646
  }
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;
647
+ init() {
648
+ var e, i;
649
+ if (this.container) return;
650
+ this.injectOverlayStyles();
651
+ const t = document.getElementById("foisit-overlay-container");
652
+ if (t && t instanceof HTMLElement) {
653
+ this.container = t, this.chatWindow = t.querySelector(".foisit-chat"), this.messagesContainer = t.querySelector(".foisit-messages"), this.input = t.querySelector("input.foisit-input"), ((e = this.config.floatingButton) == null ? void 0 : e.visible) !== !1 && !t.querySelector(".foisit-floating-btn") && this.renderFloatingButton(), this.chatWindow || this.renderChatWindow();
654
+ return;
655
+ }
656
+ this.container = document.createElement("div"), this.container.id = "foisit-overlay-container", this.container.className = "foisit-overlay-container", document.body.appendChild(this.container), ((i = this.config.floatingButton) == null ? void 0 : i.visible) !== !1 && this.renderFloatingButton(), this.renderChatWindow();
407
657
  }
408
- /**
409
- * Register callbacks for the overlay (used by floating button)
410
- */
411
- registerCallbacks(e, t) {
412
- this.submitCallback = e, this.closeCallback = t || null;
658
+ renderFloatingButton() {
659
+ var s, n, o, r, c, f;
660
+ const t = document.createElement("button");
661
+ t.innerHTML = ((s = this.config.floatingButton) == null ? void 0 : s.customHtml) || "🎙️";
662
+ const e = ((o = (n = this.config.floatingButton) == null ? void 0 : n.position) == null ? void 0 : o.bottom) || "20px", i = ((c = (r = this.config.floatingButton) == null ? void 0 : r.position) == null ? void 0 : c.right) || "20px";
663
+ t.className = "foisit-floating-btn", t.style.bottom = e, t.style.right = i, t.onclick = () => this.toggle(), t.onmouseenter = () => t.style.transform = "scale(1.05)", t.onmouseleave = () => t.style.transform = "scale(1)", (f = this.container) == null || f.appendChild(t);
413
664
  }
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);
665
+ renderChatWindow() {
666
+ var n;
667
+ if (this.chatWindow) return;
668
+ this.chatWindow = document.createElement("div"), this.chatWindow.className = "foisit-chat";
669
+ const t = document.createElement("div");
670
+ t.className = "foisit-header";
671
+ const e = document.createElement("span");
672
+ e.className = "foisit-title", e.textContent = "Foisit";
673
+ const i = document.createElement("button");
674
+ i.type = "button", i.className = "foisit-close", i.setAttribute("aria-label", "Close"), i.innerHTML = "&times;", i.addEventListener("click", () => this.toggle()), t.appendChild(e), t.appendChild(i), this.messagesContainer = document.createElement("div"), this.messagesContainer.className = "foisit-messages";
675
+ const s = document.createElement("div");
676
+ s.className = "foisit-input-area", this.input = document.createElement("input"), this.input.placeholder = this.config.inputPlaceholder || "Type a command...", this.input.className = "foisit-input", this.input.addEventListener("keydown", (o) => {
677
+ var r;
678
+ if (o.key === "Enter" && ((r = this.input) != null && r.value.trim())) {
679
+ const c = this.input.value.trim();
680
+ this.input.value = "", this.onSubmit && this.onSubmit(c);
681
+ }
682
+ }), s.appendChild(this.input), this.chatWindow.appendChild(t), this.chatWindow.appendChild(this.messagesContainer), this.chatWindow.appendChild(s), (n = this.container) == null || n.appendChild(this.chatWindow);
426
683
  }
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());
684
+ registerCallbacks(t, e) {
685
+ this.onSubmit = t, this.onClose = e;
435
686
  }
436
- /**
437
- * Toggle the overlay
438
- */
439
- toggle(e, t) {
440
- this.isOpen ? this.hide() : this.show(e, t);
687
+ toggle(t, e) {
688
+ t && (this.onSubmit = t), e && (this.onClose = e), this.isOpen = !this.isOpen, this.chatWindow && (this.isOpen ? (this.chatWindow.style.display = "flex", requestAnimationFrame(() => {
689
+ this.chatWindow && (this.chatWindow.style.opacity = "1", this.chatWindow.style.transform = "translateY(0) scale(1)");
690
+ }), setTimeout(() => {
691
+ var i;
692
+ return (i = this.input) == null ? void 0 : i.focus();
693
+ }, 100)) : (this.chatWindow.style.opacity = "0", this.chatWindow.style.transform = "translateY(20px) scale(0.95)", setTimeout(() => {
694
+ this.chatWindow && !this.isOpen && (this.chatWindow.style.display = "none");
695
+ }, 200), this.onClose && this.onClose()));
441
696
  }
442
- /**
443
- * Add a message bubble to the chat
444
- */
445
- addMessage(e, t = "system") {
446
- if (!this.chatContainer) return;
697
+ addMessage(t, e) {
698
+ if (!this.messagesContainer) return;
447
699
  const i = document.createElement("div");
448
- i.className = `foisit-bubble ${t}`, i.textContent = e, this.chatContainer.appendChild(i), this.scrollToBottom();
700
+ i.textContent = t, i.className = e === "user" ? "foisit-bubble user" : "foisit-bubble system", this.messagesContainer.appendChild(i), this.scrollToBottom();
449
701
  }
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) => {
702
+ addOptions(t) {
703
+ if (!this.messagesContainer) return;
704
+ const e = document.createElement("div");
705
+ e.className = "foisit-options-container", t.forEach((i) => {
457
706
  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);
707
+ s.textContent = i.label, s.className = "foisit-option-chip", s.setAttribute("type", "button"), s.setAttribute("aria-label", i.label);
708
+ const n = () => {
709
+ if (i.commandId) {
710
+ this.onSubmit && this.onSubmit({ commandId: i.commandId });
711
+ return;
712
+ }
713
+ const o = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
714
+ this.onSubmit && this.onSubmit(o);
715
+ };
716
+ s.onclick = n, s.onkeydown = (o) => {
717
+ (o.key === "Enter" || o.key === " ") && (o.preventDefault(), n());
718
+ }, e.appendChild(s);
719
+ }), this.messagesContainer.appendChild(e), this.scrollToBottom();
482
720
  }
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);
721
+ addForm(t, e, i) {
722
+ if (!this.messagesContainer) return;
723
+ this.addMessage(t, "system");
724
+ const s = document.createElement("form");
725
+ s.className = "foisit-form";
726
+ const n = [], o = (a, p) => {
727
+ const g = document.createElement("div");
728
+ return g.className = "foisit-form-label", g.innerHTML = a + (p ? ' <span class="foisit-req-star">*</span>' : ""), g;
729
+ }, r = () => {
730
+ const a = document.createElement("div");
731
+ return a.className = "foisit-form-error", a.style.display = "none", a;
732
+ };
733
+ (e ?? []).forEach((a) => {
734
+ const p = document.createElement("div");
735
+ p.className = "foisit-form-group";
736
+ const g = a.description || a.name;
737
+ p.appendChild(o(g, a.required));
738
+ let m;
739
+ if (a.type === "select") {
740
+ const l = document.createElement("select");
741
+ l.className = "foisit-form-input";
742
+ const b = document.createElement("option");
743
+ b.value = "", b.textContent = "Select...", l.appendChild(b);
744
+ const x = (h) => {
745
+ (h ?? []).forEach((w) => {
746
+ const y = document.createElement("option");
747
+ y.value = String(w.value ?? w.label ?? ""), y.textContent = String(w.label ?? w.value ?? ""), l.appendChild(y);
748
+ });
749
+ };
750
+ if (Array.isArray(a.options) && a.options.length)
751
+ x(a.options);
752
+ else if (typeof a.getOptions == "function") {
753
+ const h = a.getOptions, w = document.createElement("option");
754
+ w.value = "", w.textContent = "Loading...", l.appendChild(w), Promise.resolve().then(() => h()).then((y) => {
755
+ for (; l.options.length > 1; ) l.remove(1);
756
+ x(y);
757
+ }).catch(() => {
758
+ for (; l.options.length > 1; ) l.remove(1);
759
+ const y = document.createElement("option");
760
+ y.value = "", y.textContent = "Error loading options", l.appendChild(y);
761
+ });
762
+ }
763
+ a.defaultValue != null && (l.value = String(a.defaultValue)), m = l;
764
+ } else if (a.type === "file") {
765
+ const l = a, b = document.createElement("input");
766
+ b.className = "foisit-form-input", b.type = "file", l.accept && Array.isArray(l.accept) && (b.accept = l.accept.join(",")), l.multiple && (b.multiple = !0), l.capture && (l.capture === !0 ? b.setAttribute("capture", "") : b.setAttribute("capture", String(l.capture))), b.addEventListener("change", async () => {
767
+ const x = Array.from(b.files || []), h = C;
768
+ if (h.style.display = "none", h.textContent = "", x.length === 0) return;
769
+ const w = l.maxFiles ?? (l.multiple ? 10 : 1);
770
+ if (x.length > w) {
771
+ h.textContent = `Please select at most ${w} file(s).`, h.style.display = "block";
772
+ return;
773
+ }
774
+ const y = l.maxSizeBytes ?? 1 / 0, u = x.reduce((v, A) => v + A.size, 0);
775
+ if (x.some((v) => v.size > y)) {
776
+ h.textContent = `One or more files exceed the maximum size of ${Math.round(y / 1024)} KB.`, h.style.display = "block";
777
+ return;
778
+ }
779
+ const S = l.maxTotalBytes ?? 1 / 0;
780
+ if (u > S) {
781
+ h.textContent = `Total selected files exceed the maximum of ${Math.round(S / 1024)} KB.`, h.style.display = "block";
782
+ return;
783
+ }
784
+ if (l.accept && Array.isArray(l.accept)) {
785
+ const v = l.accept;
786
+ if (!x.every((E) => E.type ? v.some((L) => L.startsWith(".") ? E.name.toLowerCase().endsWith(L.toLowerCase()) : E.type === L || E.type.startsWith(L.split("/")[0] + "/")) : !0)) {
787
+ h.textContent = "One or more files have an unsupported type.", h.style.display = "block";
788
+ return;
789
+ }
790
+ }
791
+ }), m = b;
792
+ } else {
793
+ const l = document.createElement("input");
794
+ l.className = "foisit-form-input", a.type === "string" && (l.placeholder = a.placeholder || "Type here..."), a.type === "number" ? (l.type = "number", typeof a.min == "number" && (l.min = String(a.min)), typeof a.max == "number" && (l.max = String(a.max)), typeof a.step == "number" && (l.step = String(a.step)), a.defaultValue != null && (l.value = String(a.defaultValue))) : a.type === "date" ? (l.type = "date", typeof a.min == "string" && (l.min = a.min), typeof a.max == "string" && (l.max = a.max), a.defaultValue != null && (l.value = String(a.defaultValue))) : (l.type = "text", a.defaultValue != null && (l.value = String(a.defaultValue))), m = l;
795
+ }
796
+ const C = r();
797
+ p.appendChild(m), p.appendChild(C), n.push({
798
+ name: a.name,
799
+ type: a.type,
800
+ el: m,
801
+ required: a.required
802
+ }), s.appendChild(p);
510
803
  });
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();
804
+ const c = document.createElement("div");
805
+ c.className = "foisit-form-actions";
806
+ const f = document.createElement("button");
807
+ f.type = "submit", f.textContent = "Submit", f.className = "foisit-option-chip", f.style.fontWeight = "600", c.appendChild(f), s.appendChild(c), s.onsubmit = async (a) => {
808
+ a.preventDefault();
809
+ const p = {};
810
+ let g = !1;
811
+ s.querySelectorAll(".foisit-form-error").forEach((m) => {
812
+ m.style.display = "none", m.textContent = "";
813
+ }), s.querySelectorAll(".foisit-form-input").forEach((m) => {
814
+ m.classList.remove("foisit-error-border");
815
+ });
816
+ for (const m of n) {
817
+ if (m.type === "file") {
818
+ const x = m.el.parentElement, h = x == null ? void 0 : x.querySelector(".foisit-form-error"), w = m.el, y = Array.from(w.files || []);
819
+ if (m.required && y.length === 0) {
820
+ g = !0, w.classList.add("foisit-error-border"), h && (h.textContent = "This file is required", h.style.display = "block");
821
+ continue;
822
+ }
823
+ if (y.length === 0) continue;
824
+ const u = (e ?? []).find((v) => v.name === m.name), S = (u == null ? void 0 : u.delivery) ?? "file";
825
+ if (u != null && u.maxWidth || u != null && u.maxHeight)
826
+ try {
827
+ const v = await this.getImageDimensions(y[0]);
828
+ if (u.maxWidth && v.width > u.maxWidth) {
829
+ g = !0, h && (h.textContent = `Image width must be ≤ ${u.maxWidth}px`, h.style.display = "block");
830
+ continue;
831
+ }
832
+ if (u.maxHeight && v.height > u.maxHeight) {
833
+ g = !0, h && (h.textContent = `Image height must be ≤ ${u.maxHeight}px`, h.style.display = "block");
834
+ continue;
835
+ }
836
+ } catch {
837
+ }
838
+ if (u != null && u.maxDurationSec)
839
+ try {
840
+ const v = await this.getMediaDuration(y[0]);
841
+ if (v && v > u.maxDurationSec) {
842
+ g = !0, h && (h.textContent = `Media duration must be ≤ ${u.maxDurationSec}s`, h.style.display = "block");
843
+ continue;
844
+ }
845
+ } catch {
846
+ }
847
+ if (S === "file")
848
+ p[m.name] = u != null && u.multiple ? y : y[0];
849
+ else if (S === "base64")
850
+ try {
851
+ const v = await Promise.all(y.map((A) => this.readFileAsDataURL(A)));
852
+ p[m.name] = u != null && u.multiple ? v : v[0];
853
+ } catch {
854
+ g = !0, h && (h.textContent = "Failed to encode file(s) to base64.", h.style.display = "block");
855
+ continue;
856
+ }
857
+ continue;
858
+ }
859
+ const C = (m.el.value ?? "").toString().trim(), l = m.el.parentElement, b = l == null ? void 0 : l.querySelector(".foisit-form-error");
860
+ if (m.required && (C == null || C === "")) {
861
+ g = !0, m.el.classList.add("foisit-error-border"), b && (b.textContent = "This field is required", b.style.display = "block");
862
+ continue;
863
+ }
864
+ if (C !== "")
865
+ if (m.type === "number") {
866
+ const x = Number(C);
867
+ Number.isNaN(x) || (p[m.name] = x);
868
+ } else
869
+ p[m.name] = C;
870
+ }
871
+ if (g) {
872
+ s.classList.add("foisit-shake"), setTimeout(() => s.classList.remove("foisit-shake"), 400);
873
+ return;
874
+ }
875
+ f.disabled = !0, f.style.opacity = "0.6", n.forEach((m) => {
876
+ m.el.disabled = !0;
877
+ }), i(p);
878
+ }, this.messagesContainer.appendChild(s), this.scrollToBottom();
518
879
  }
519
- /**
520
- * Show loading indicator
521
- */
522
880
  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();
881
+ if (this.messagesContainer && !this.loadingEl) {
882
+ this.loadingEl = document.createElement("div"), this.loadingEl.className = "foisit-loading-dots foisit-bubble system";
883
+ for (let t = 0; t < 3; t++) {
884
+ const e = document.createElement("div");
885
+ e.className = "foisit-dot", e.style.animation = `foisitPulse 1.4s infinite ease-in-out ${t * 0.2}s`, this.loadingEl.appendChild(e);
886
+ }
887
+ this.messagesContainer.appendChild(this.loadingEl), this.scrollToBottom();
888
+ }
526
889
  }
527
- /**
528
- * Hide loading indicator
529
- */
530
890
  hideLoading() {
531
- const e = document.getElementById("foisit-loading-bubble");
532
- e && e.remove();
891
+ var t;
892
+ (t = this.loadingEl) == null || t.remove(), this.loadingEl = null;
533
893
  }
534
894
  scrollToBottom() {
535
- this.chatContainer && (this.chatContainer.scrollTop = this.chatContainer.scrollHeight);
895
+ this.messagesContainer && (this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight);
536
896
  }
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);
897
+ destroy() {
898
+ var t;
899
+ (t = this.container) == null || t.remove(), this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1;
900
+ }
901
+ readFileAsDataURL(t) {
902
+ return new Promise((e, i) => {
903
+ const s = new FileReader();
904
+ s.onerror = () => i(new Error("Failed to read file")), s.onload = () => e(String(s.result)), s.readAsDataURL(t);
905
+ });
906
+ }
907
+ getImageDimensions(t) {
908
+ return new Promise((e) => {
909
+ try {
910
+ const i = URL.createObjectURL(t), s = new Image();
911
+ s.onload = () => {
912
+ const n = { width: s.naturalWidth || s.width, height: s.naturalHeight || s.height };
913
+ URL.revokeObjectURL(i), e(n);
914
+ }, s.onerror = () => {
915
+ URL.revokeObjectURL(i), e({ width: 0, height: 0 });
916
+ }, s.src = i;
917
+ } catch {
918
+ e({ width: 0, height: 0 });
919
+ }
920
+ });
921
+ }
922
+ getMediaDuration(t) {
923
+ return new Promise((e) => {
924
+ try {
925
+ const i = URL.createObjectURL(t), s = t.type.startsWith("audio") ? document.createElement("audio") : document.createElement("video");
926
+ let n = !1;
927
+ const o = setTimeout(() => {
928
+ n || (n = !0, URL.revokeObjectURL(i), e(0));
929
+ }, 5e3);
930
+ s.preload = "metadata", s.onloadedmetadata = () => {
931
+ if (n) return;
932
+ n = !0, clearTimeout(o);
933
+ const c = s.duration || 0;
934
+ URL.revokeObjectURL(i), e(c);
935
+ }, s.onerror = () => {
936
+ n || (n = !0, clearTimeout(o), URL.revokeObjectURL(i), e(0));
937
+ }, s.src = i;
938
+ } catch {
939
+ e(0);
552
940
  }
941
+ });
942
+ }
943
+ injectOverlayStyles() {
944
+ if (document.getElementById("foisit-overlay-styles")) return;
945
+ const t = document.createElement("style");
946
+ t.id = "foisit-overlay-styles", t.textContent = `
947
+ :root {
948
+ /* LIGHT MODE (Default) - Smoother gradient */
949
+ /* Changed: Softer, right-focused radial highlight to avoid a heavy white bottom */
950
+ --foisit-bg: radial-gradient(ellipse at 75% 30%, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.03));
951
+ --foisit-border: 1px solid rgba(255, 255, 255, 0.25);
952
+ --foisit-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
953
+ --foisit-text: #333;
954
+
955
+ /* Input */
956
+ --foisit-input-color: #333;
957
+ --foisit-input-placeholder: rgba(60, 60, 67, 0.6);
958
+
959
+ /* Bubbles */
960
+ --foisit-bubble-user-bg: rgba(0, 0, 0, 0.04);
961
+ --foisit-bubble-user-text: #333;
553
962
 
554
- #${l.OVERLAY_ID}.visible {
555
- opacity: 1;
556
- transform: translateY(0);
963
+ --foisit-bubble-sys-bg: rgba(255, 255, 255, 0.45);
964
+ --foisit-bubble-sys-text: #333;
965
+
966
+ /* Form Colors */
967
+ --foisit-req-star: #ef4444; /* Red asterisk */
968
+ --foisit-error-text: #dc2626;
969
+ --foisit-error-border: #fca5a5;
557
970
  }
558
971
 
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;
972
+ @media (prefers-color-scheme: dark) {
973
+ :root {
974
+ /* DARK MODE */
975
+ --foisit-bg: linear-gradient(135deg, rgba(40, 40, 40, 0.65), rgba(40, 40, 40, 0.25));
976
+ --foisit-border: 1px solid rgba(255, 255, 255, 0.1);
977
+ --foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
978
+ --foisit-text: #fff;
979
+
980
+ /* Input */
981
+ --foisit-input-color: white;
982
+ --foisit-input-placeholder: rgba(235, 235, 245, 0.5);
983
+
984
+ /* Bubbles */
985
+ --foisit-bubble-user-bg: rgba(255, 255, 255, 0.1);
986
+ --foisit-bubble-user-text: white;
987
+
988
+ --foisit-bubble-sys-bg: rgba(255, 255, 255, 0.05);
989
+ --foisit-bubble-sys-text: rgba(255, 255, 255, 0.9);
990
+
991
+ /* Form Colors */
992
+ --foisit-req-star: #f87171;
993
+ --foisit-error-text: #fca5a5;
994
+ --foisit-error-border: #f87171;
995
+ }
576
996
  }
577
997
 
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;
998
+ @keyframes foisitPulse {
999
+ 0%, 100% { transform: scale(0.8); opacity: 0.5; }
1000
+ 50% { transform: scale(1.2); opacity: 1; }
592
1001
  }
593
1002
 
594
- #${l.INPUT_ID}::placeholder {
595
- color: rgba(60, 60, 67, 0.7);
596
- font-weight: 500;
1003
+ @keyframes foisitShake {
1004
+ 0%, 100% { transform: translateX(0); }
1005
+ 25% { transform: translateX(-4px); }
1006
+ 75% { transform: translateX(4px); }
597
1007
  }
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
- }
1008
+ .foisit-shake { animation: foisitShake 0.4s ease-in-out; }
1009
+
1010
+ /* Container */
1011
+ .foisit-overlay-container {
1012
+ position: fixed;
1013
+ inset: 0;
1014
+ z-index: 2147483647;
1015
+ pointer-events: none;
1016
+ display: flex;
1017
+ flex-direction: column;
1018
+ justify-content: flex-end;
1019
+ align-items: flex-end;
1020
+ padding: 20px;
1021
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
1022
+ }
1023
+
1024
+ .foisit-overlay-container * {
1025
+ box-sizing: border-box;
617
1026
  }
618
1027
 
619
- .foisit-watermark {
620
- align-self: flex-end;
621
- margin: 0 12px 10px auto;
622
- padding: 4px 8px;
623
- border-radius: 8px;
1028
+ /* Chat Window - Dynamic Height */
1029
+ .foisit-chat {
1030
+ position: absolute;
1031
+ top: 20px;
1032
+ right: 20px;
1033
+ width: min(420px, 92vw);
624
1034
 
625
- font-size: 9px;
626
- font-weight: 600;
627
- letter-spacing: 0.8px;
628
- text-transform: capitalize;
1035
+ /* FIX: Auto height to prevent empty space */
1036
+ height: auto;
1037
+ min-height: 120px;
1038
+ max-height: 80vh;
629
1039
 
630
- background: rgba(0, 0, 0, 0.35);
631
- backdrop-filter: blur(6px);
632
- -webkit-backdrop-filter: blur(6px);
1040
+ background: var(--foisit-bg);
1041
+ border: var(--foisit-border);
1042
+ box-shadow: var(--foisit-shadow);
633
1043
 
634
- color: rgba(255, 255, 255, 0.85);
635
- pointer-events: none;
636
- }
1044
+ backdrop-filter: blur(20px);
1045
+ -webkit-backdrop-filter: blur(20px);
1046
+
1047
+ border-radius: 18px;
1048
+ display: none;
1049
+ flex-direction: column;
1050
+ overflow: hidden;
1051
+ pointer-events: auto;
1052
+ transform-origin: top right;
1053
+ transition: opacity 0.2s, transform 0.2s cubic-bezier(0.2, 0.9, 0.2, 1);
1054
+ }
1055
+
1056
+ .foisit-header {
1057
+ display: flex;
1058
+ align-items: center;
1059
+ justify-content: space-between;
1060
+ padding: 12px 16px;
1061
+ font-weight: 600;
1062
+ font-size: 14px;
1063
+ color: var(--foisit-text);
1064
+ border-bottom: 1px solid rgba(127,127,127,0.08); /* Subtle separator */
1065
+ }
637
1066
 
1067
+ .foisit-close {
1068
+ background: transparent;
1069
+ border: none;
1070
+ color: var(--foisit-text);
1071
+ opacity: 0.5;
1072
+ font-size: 24px;
1073
+ line-height: 1;
1074
+ cursor: pointer;
1075
+ padding: 0;
1076
+ width: 28px;
1077
+ height: 28px;
1078
+ display: flex;
1079
+ align-items: center;
1080
+ justify-content: center;
1081
+ transition: opacity 0.2s;
1082
+ }
1083
+ .foisit-close:hover { opacity: 1; }
638
1084
 
639
- /* Chat UI Styles */
640
- #foisit-chat-history {
1085
+ /* Message Area */
1086
+ .foisit-messages {
641
1087
  flex: 1;
642
1088
  overflow-y: auto;
643
1089
  padding: 16px;
644
1090
  display: flex;
645
1091
  flex-direction: column;
646
- gap: 12px;
647
- max-height: 400px;
1092
+ gap: 10px;
1093
+ /* Ensure it doesn't get too tall initially */
1094
+ min-height: 60px;
1095
+ }
1096
+
1097
+ /* Make sure empty state isn't huge */
1098
+ .foisit-messages:empty {
1099
+ display: none;
1100
+ }
1101
+
1102
+ /* Only show messages container if it has children */
1103
+ .foisit-messages:not(:empty) {
1104
+ display: flex;
648
1105
  }
649
1106
 
1107
+ /* Bubbles */
650
1108
  .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;
1109
+ max-width: 90%;
1110
+ padding: 8px 14px;
1111
+ border-radius: 14px;
655
1112
  font-size: 14px;
656
1113
  line-height: 1.4;
1114
+ word-wrap: break-word;
657
1115
  }
658
1116
 
659
1117
  .foisit-bubble.user {
660
1118
  align-self: flex-end;
661
- background: rgba(0, 0, 0, 0.05); /* Subtle dark for user */
662
- color: #333;
1119
+ background: var(--foisit-bubble-user-bg);
1120
+ color: var(--foisit-bubble-user-text);
663
1121
  border-bottom-right-radius: 4px;
664
1122
  }
665
1123
 
666
1124
  .foisit-bubble.system {
667
1125
  align-self: flex-start;
668
- background: rgba(255, 255, 255, 0.4); /* Glassy white */
669
- color: #333;
1126
+ background: var(--foisit-bubble-sys-bg);
1127
+ color: var(--foisit-bubble-sys-text);
670
1128
  border-bottom-left-radius: 4px;
1129
+ border: 1px solid rgba(255,255,255,0.1);
1130
+ }
1131
+
1132
+ /* Input Area */
1133
+ .foisit-input-area {
1134
+ padding: 0;
1135
+ width: 100%;
1136
+ border-top: 1px solid rgba(127,127,127,0.08);
1137
+ }
1138
+
1139
+ .foisit-input {
1140
+ width: 100%;
1141
+ background: transparent;
1142
+ border: none;
1143
+ font-size: 16px;
1144
+ color: var(--foisit-input-color);
1145
+ padding: 16px 20px;
1146
+ outline: none;
1147
+ text-align: left;
671
1148
  }
672
1149
 
1150
+ .foisit-input::placeholder {
1151
+ color: var(--foisit-input-placeholder);
1152
+ font-weight: 400;
1153
+ }
1154
+
1155
+ /* Options & Buttons */
673
1156
  .foisit-options-container {
674
1157
  display: flex;
675
1158
  flex-wrap: wrap;
676
1159
  gap: 8px;
677
- margin-left: 4px;
1160
+ margin-left: 2px;
1161
+ margin-top: 4px;
678
1162
  }
679
1163
 
680
1164
  .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);
1165
+ padding: 6px 14px;
1166
+ background: var(--foisit-bubble-sys-bg);
1167
+ border: 1px solid rgba(127,127,127,0.1);
684
1168
  border-radius: 20px;
685
1169
  font-size: 13px;
686
- color: #333;
1170
+ color: var(--foisit-text);
687
1171
  cursor: pointer;
688
1172
  transition: all 0.2s;
1173
+ font-weight: 500;
689
1174
  }
690
-
691
1175
  .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); }
1176
+ background: rgba(127,127,127,0.15);
738
1177
  }
739
1178
 
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);
1179
+ /* Form Styling */
1180
+ .foisit-form {
1181
+ background: var(--foisit-bubble-sys-bg);
823
1182
  padding: 16px;
824
- border-radius: 12px;
825
- width: 100%;
826
- box-sizing: border-box;
827
- border: 1px solid rgba(255, 255, 255, 0.2);
1183
+ border-radius: 14px;
828
1184
  display: flex;
829
1185
  flex-direction: column;
830
1186
  gap: 12px;
1187
+ width: 100%;
1188
+ border: 1px solid rgba(127,127,127,0.1);
831
1189
  }
832
1190
 
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;
1191
+ .foisit-form-label {
1192
+ font-size: 12px;
1193
+ font-weight: 600;
1194
+ color: var(--foisit-text);
1195
+ opacity: 0.9;
1196
+ margin-bottom: 2px;
845
1197
  }
846
1198
 
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);
1199
+ .foisit-req-star {
1200
+ color: var(--foisit-req-star);
1201
+ font-weight: bold;
852
1202
  }
853
1203
 
854
- .foisit-field-input {
855
- background: rgba(255, 255, 255, 0.6);
856
- border: 1px solid rgba(0, 0, 0, 0.1);
1204
+ .foisit-form-input {
1205
+ width: 100%;
1206
+ padding: 10px;
857
1207
  border-radius: 8px;
858
- padding: 8px 12px;
1208
+ border: 1px solid rgba(127,127,127,0.2);
1209
+ background: rgba(255,255,255,0.05); /* Very subtle fill */
1210
+ color: var(--foisit-text);
859
1211
  font-size: 14px;
860
- color: #333;
861
1212
  outline: none;
1213
+ transition: border 0.2s;
1214
+ }
1215
+ .foisit-form-input:focus {
1216
+ border-color: var(--foisit-text);
1217
+ background: rgba(255,255,255,0.1);
862
1218
  }
863
1219
 
864
- .foisit-field-input:focus {
865
- border-color: rgba(0, 0, 0, 0.3);
1220
+ .foisit-error-border {
1221
+ border-color: var(--foisit-error-border) !important;
866
1222
  }
867
1223
 
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;
1224
+ .foisit-form-error {
1225
+ font-size: 11px;
1226
+ color: var(--foisit-error-text);
878
1227
  margin-top: 4px;
879
1228
  }
880
1229
 
881
- .foisit-form-submit:hover {
882
- opacity: 0.8;
1230
+ /* Loading */
1231
+ .foisit-loading-dots {
1232
+ display: inline-flex;
1233
+ gap: 4px;
1234
+ padding: 10px 14px;
1235
+ align-self: flex-start;
883
1236
  }
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
- }
1237
+ .foisit-dot {
1238
+ width: 6px;
1239
+ height: 6px;
1240
+ border-radius: 50%;
1241
+ background: var(--foisit-text);
1242
+ opacity: 0.4;
905
1243
  }
906
1244
 
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");
1245
+ /* Floating Button */
1246
+ .foisit-floating-btn {
1247
+ position: absolute;
1248
+ width: 56px;
1249
+ height: 56px;
1250
+ border-radius: 50%;
1251
+ border: 1px solid rgba(255,255,255,0.2);
1252
+ background: var(--foisit-bg);
1253
+ color: var(--foisit-text);
1254
+ backdrop-filter: blur(10px);
1255
+ -webkit-backdrop-filter: blur(10px);
1256
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
1257
+ cursor: pointer;
1258
+ pointer-events: auto;
1259
+ display: flex;
1260
+ align-items: center;
1261
+ justify-content: center;
1262
+ font-size: 24px;
1263
+ z-index: 100000;
1264
+ transition: transform 0.2s;
933
1265
  }
934
- i.key === "Escape" && this.hide();
935
- }));
1266
+ .foisit-floating-btn:hover { transform: scale(1.05); }
1267
+ `, document.head.appendChild(t);
936
1268
  }
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({
1269
+ }
1270
+ class Y {
1271
+ constructor(t) {
1272
+ this.config = t, this.isActivated = !1, this.lastProcessedInput = "", this.processingLock = !1, this.defaultIntroMessage = "How can I help you?", this.commandHandler = new B({
1273
+ enableSmartIntent: this.config.enableSmartIntent !== !1,
1274
+ intentEndpoint: this.config.intentEndpoint
1275
+ }), this.fallbackHandler = new z(), this.voiceProcessor = new O(), this.textToSpeech = new q(), this.stateManager = new V(), this.gestureHandler = new U(), this.overlayManager = new G({
945
1276
  floatingButton: this.config.floatingButton,
946
1277
  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);
1278
+ }), this.config.commands.forEach((e) => this.commandHandler.addCommand(e)), this.config.fallbackResponse && this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse), this.gestureHandler.setupDoubleTapListener(() => this.toggle()), this.overlayManager.registerCallbacks(
1279
+ async (e) => {
1280
+ if (typeof e == "string") {
1281
+ this.overlayManager.addMessage(e, "user"), await this.handleCommand(e);
1282
+ return;
1283
+ }
1284
+ if (e && typeof e == "object") {
1285
+ const i = e, s = i.label ?? i.commandId ?? "Selection";
1286
+ this.overlayManager.addMessage(String(s), "user"), this.overlayManager.showLoading();
1287
+ const n = await this.commandHandler.executeCommand(i);
1288
+ this.overlayManager.hideLoading(), this.processResponse(n);
1289
+ }
950
1290
  },
951
1291
  () => console.log("AssistantService: Overlay closed.")
952
1292
  );
953
1293
  }
954
1294
  /** Start listening for activation and commands */
955
1295
  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
- });
1296
+ console.log("AssistantService: Voice is disabled; startListening() is a no-op.");
970
1297
  }
971
1298
  /** Stop listening */
972
1299
  stopListening() {
973
- console.log("AssistantService: Stopping listening..."), this.voiceProcessor.stopListening(), this.isActivated = !1;
1300
+ console.log("AssistantService: Voice is disabled; stopListening() is a no-op."), this.isActivated = !1;
1301
+ }
1302
+ /**
1303
+ * Reset activation state so the next activation flow can occur.
1304
+ * This is mainly used by the wrapper UI (e.g. AssistantActivator).
1305
+ */
1306
+ reactivate() {
1307
+ console.log("AssistantService: Reactivating assistant..."), this.isActivated = !1;
1308
+ try {
1309
+ this.startListening();
1310
+ } catch {
1311
+ }
974
1312
  }
975
1313
  /** Process activation command */
976
- async processActivation(e) {
1314
+ async processActivation(t) {
977
1315
  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(
1316
+ const e = (i = this.config.activationCommand) == null ? void 0 : i.toLowerCase();
1317
+ e && (t === e ? (console.log("AssistantService: Activation matched."), this.isActivated = !0, this.textToSpeech.speak(
980
1318
  this.config.introMessage || this.defaultIntroMessage
981
1319
  )) : console.log("AssistantService: Activation command not recognized."));
982
1320
  }
983
1321
  /** Handle recognized commands */
984
- async handleCommand(e) {
1322
+ async handleCommand(t) {
985
1323
  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);
1324
+ let e;
1325
+ try {
1326
+ e = await this.commandHandler.executeCommand(t);
1327
+ } finally {
1328
+ this.overlayManager.hideLoading();
1329
+ }
1330
+ if (e.type === "form" && e.fields) {
1331
+ this.overlayManager.addForm(
1332
+ e.message,
1333
+ e.fields,
1334
+ async (i) => {
1335
+ this.overlayManager.showLoading();
1336
+ let s;
1337
+ try {
1338
+ s = await this.commandHandler.executeCommand(i);
1339
+ } finally {
1340
+ this.overlayManager.hideLoading();
1341
+ }
1342
+ this.processResponse(s);
1343
+ }
1344
+ );
1345
+ return;
1346
+ }
1347
+ if (e.type === "error") {
1348
+ this.fallbackHandler.handleFallback(t), this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), "system");
1349
+ return;
1350
+ }
1351
+ if ((e.type === "ambiguous" || e.type === "confirm") && e.options) {
1352
+ e.message && this.overlayManager.addMessage(e.message, "system"), this.overlayManager.addOptions(e.options);
1353
+ return;
1354
+ }
1355
+ e.message && this.overlayManager.addMessage(e.message, "system");
996
1356
  }
997
1357
  /**
998
1358
  * Cleanup resources
999
1359
  */
1000
1360
  destroy() {
1001
- this.voiceProcessor.stopListening(), this.overlayManager.destroy();
1361
+ this.voiceProcessor.stopListening(), this.gestureHandler.destroy(), this.overlayManager.destroy();
1002
1362
  }
1003
1363
  /** 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);
1364
+ processResponse(t) {
1365
+ if (t) {
1366
+ if (t.type === "form" && t.fields) {
1367
+ this.overlayManager.addForm(
1368
+ t.message,
1369
+ t.fields,
1370
+ async (e) => {
1371
+ this.overlayManager.showLoading();
1372
+ let i;
1373
+ try {
1374
+ i = await this.commandHandler.executeCommand(e);
1375
+ } finally {
1376
+ this.overlayManager.hideLoading();
1377
+ }
1378
+ this.processResponse(i);
1379
+ }
1380
+ );
1381
+ return;
1012
1382
  }
1013
- ) : e.type === "ambiguous" && e.options && this.overlayManager.addOptions(e.options);
1383
+ if ((t.type === "ambiguous" || t.type === "confirm") && t.options) {
1384
+ t.message && this.overlayManager.addMessage(t.message, "system"), this.overlayManager.addOptions(t.options);
1385
+ return;
1386
+ }
1387
+ t.message && this.overlayManager.addMessage(t.message, "system");
1388
+ }
1014
1389
  }
1015
1390
  /** 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);
1391
+ addCommand(t, e) {
1392
+ console.log(typeof t == "string" ? `AssistantService: Adding command "${t}".` : `AssistantService: Adding rich command "${t.command}".`), this.commandHandler.addCommand(t, e);
1018
1393
  }
1019
1394
  /** Remove a command dynamically */
1020
- removeCommand(e) {
1021
- console.log(`AssistantService: Removing command "${e}".`), this.commandHandler.removeCommand(e);
1395
+ removeCommand(t) {
1396
+ console.log(`AssistantService: Removing command "${t}".`), this.commandHandler.removeCommand(t);
1022
1397
  }
1023
1398
  /** Get all registered commands */
1024
1399
  getCommands() {
1025
1400
  return this.commandHandler.getCommands();
1026
1401
  }
1027
1402
  /** Toggle the assistant overlay */
1028
- toggle(e, t) {
1403
+ toggle(t, e) {
1029
1404
  console.log("AssistantService: Toggling overlay..."), this.overlayManager.toggle(
1030
1405
  async (i) => {
1031
- this.overlayManager.addMessage(i, "user"), e && e(i), await this.handleCommand(i);
1406
+ if (typeof i == "string") {
1407
+ this.overlayManager.addMessage(i, "user"), t && t(i), await this.handleCommand(i);
1408
+ return;
1409
+ }
1410
+ if (i && typeof i == "object") {
1411
+ const s = i, n = s.label ?? s.commandId ?? "Selection";
1412
+ this.overlayManager.addMessage(String(n), "user"), this.overlayManager.showLoading();
1413
+ let o;
1414
+ try {
1415
+ o = await this.commandHandler.executeCommand(s);
1416
+ } finally {
1417
+ this.overlayManager.hideLoading();
1418
+ }
1419
+ this.processResponse(o);
1420
+ }
1032
1421
  },
1033
1422
  () => {
1034
- console.log("AssistantService: Overlay closed."), t && t();
1423
+ console.log("AssistantService: Overlay closed."), e && e();
1035
1424
  }
1036
1425
  );
1037
1426
  }
1038
1427
  }
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;
1428
+ const H = F(null);
1429
+ let I = null;
1430
+ const J = ({ config: d, children: t }) => {
1431
+ const [e, i] = R(null), [s, n] = R(!1);
1432
+ return P(() => {
1433
+ I ? console.warn(
1434
+ "Multiple AssistantProvider instances detected. Reusing global AssistantService."
1435
+ ) : (console.log("Initializing global AssistantService..."), I = new Y(d));
1436
+ const o = I;
1437
+ return i(o), n(!0), () => {
1438
+ var r;
1439
+ console.log("Cleaning up AssistantService..."), (r = o.destroy) == null || r.call(o), I = null;
1049
1440
  };
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)
1441
+ }, [d]), !s || !e ? /* @__PURE__ */ k("div", { children: "Loading Assistant..." }) : /* @__PURE__ */ k(H.Provider, { value: e, children: t });
1442
+ }, K = () => {
1443
+ const d = N(H);
1444
+ if (console.log("assistant", d), !d)
1054
1445
  throw new Error("useAssistant must be used within an AssistantProvider");
1055
- return r;
1056
- }, U = ({
1057
- label: r = "Activate Assistant",
1058
- onActivate: e
1446
+ return d;
1447
+ }, Z = ({
1448
+ label: d = "Activate Assistant",
1449
+ onActivate: t
1059
1450
  }) => {
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(() => {
1451
+ const e = K();
1452
+ return /* @__PURE__ */ k("button", { onClick: () => {
1453
+ t && t(), e.reactivate();
1454
+ }, className: "assistant-activator", children: d });
1455
+ }, tt = (d) => {
1456
+ const [t, e] = R(d.getState());
1457
+ return P(() => {
1067
1458
  const i = (s) => {
1068
- t(s);
1459
+ e(s);
1069
1460
  };
1070
- return r.subscribe(i), () => {
1071
- r.subscribe(() => {
1461
+ return d.subscribe(i), () => {
1462
+ d.subscribe(() => {
1072
1463
  });
1073
1464
  };
1074
- }, [r]), e;
1465
+ }, [d]), t;
1075
1466
  };
1076
1467
  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
1468
+ Z as AssistantActivator,
1469
+ H as AssistantContext,
1470
+ J as AssistantProvider,
1471
+ Y as AssistantService,
1472
+ Q as ReactWrapper,
1473
+ K as useAssistant,
1474
+ tt as useAssistantState
1084
1475
  };