@foisit/react-wrapper 2.4.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
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 J() {
5
- return /* @__PURE__ */ k("div", { className: W.container, children: /* @__PURE__ */ k("h1", { children: "Welcome to ReactWrapper!" }) });
2
+ import { createContext as F, useState as I, useEffect as H, useContext as B } from "react";
3
+ const O = {};
4
+ function Q() {
5
+ return /* @__PURE__ */ k("div", { className: O.container, children: /* @__PURE__ */ k("h1", { children: "Welcome to ReactWrapper!" }) });
6
6
  }
7
- class M {
7
+ class T {
8
8
  constructor(t) {
9
9
  this.endpoint = t || "https://foisit-ninja.netlify.app/.netlify/functions/intent";
10
10
  }
@@ -12,11 +12,11 @@ class M {
12
12
  try {
13
13
  const s = {
14
14
  userInput: t,
15
- commands: e.map((r) => ({
16
- id: r.id,
17
- command: r.command,
18
- description: r.description,
19
- parameters: r.parameters
15
+ commands: e.map((o) => ({
16
+ id: o.id,
17
+ command: o.command,
18
+ description: o.description,
19
+ parameters: o.parameters
20
20
  // Send param schemas to AI
21
21
  })),
22
22
  context: i
@@ -35,13 +35,13 @@ class M {
35
35
  }
36
36
  }
37
37
  }
38
- class B {
38
+ class N {
39
39
  constructor(t = !0) {
40
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());
41
+ this.enableSmartIntent = t, this.enableSmartIntent && (this.openAIService = new T());
42
42
  return;
43
43
  }
44
- this.enableSmartIntent = t.enableSmartIntent ?? !0, this.enableSmartIntent && (this.openAIService = new M(t.intentEndpoint));
44
+ this.enableSmartIntent = t.enableSmartIntent ?? !0, this.enableSmartIntent && (this.openAIService = new T(t.intentEndpoint));
45
45
  }
46
46
  /** Add a new command (string or object) */
47
47
  addCommand(t, e) {
@@ -66,14 +66,14 @@ class B {
66
66
  async executeCommand(t) {
67
67
  if (typeof t == "object" && t !== null) {
68
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}".`,
69
+ const c = String(t.commandId), u = t.params ?? {}, r = this.getCommandById(c);
70
+ if (!r) return { message: "That command is not available.", type: "error" };
71
+ const p = this.sanitizeParamsForCommand(r, u), m = (r.parameters ?? []).filter((C) => C.required).filter((C) => p[C.name] == null || p[C.name] === "");
72
+ return m.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(r), params: p }, {
73
+ message: `Please provide the required details for "${r.command}".`,
74
74
  type: "form",
75
75
  fields: m
76
- }) : a.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(a), params: p }, this.buildConfirmResponse(a)) : this.safeRunAction(a, p);
76
+ }) : r.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(r), params: p }, this.buildConfirmResponse(r)) : this.safeRunAction(r, p);
77
77
  }
78
78
  if (!this.context)
79
79
  return { message: "Session expired or invalid context.", type: "error" };
@@ -82,26 +82,26 @@ class B {
82
82
  return this.context = null, { message: "Session expired or invalid context.", type: "error" };
83
83
  if (Array.isArray(t))
84
84
  return { message: "Invalid form payload.", type: "error" };
85
- const o = {
85
+ const a = {
86
86
  ...this.context.params,
87
87
  ...t
88
88
  };
89
89
  if (n.critical)
90
90
  return this.context = null, this.pendingConfirmation = {
91
91
  commandId: this.getCommandIdentifier(n),
92
- params: o
92
+ params: a
93
93
  }, this.buildConfirmResponse(n);
94
- const r = await this.safeRunAction(n, o);
95
- return this.context = null, this.normalizeResponse(r);
94
+ const o = await this.safeRunAction(n, a);
95
+ return this.context = null, this.normalizeResponse(o);
96
96
  }
97
97
  const e = t.trim().toLowerCase();
98
98
  if (this.pendingConfirmation) {
99
99
  const n = e;
100
100
  if (["yes", "y", "confirm", "ok", "okay"].includes(n)) {
101
- const { commandId: o, params: r } = this.pendingConfirmation;
101
+ const { commandId: a, params: o } = this.pendingConfirmation;
102
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" };
103
+ const c = this.getCommandById(a);
104
+ return c ? this.safeRunAction(c, o) : { message: "That action is no longer available.", type: "error" };
105
105
  }
106
106
  return ["no", "n", "cancel", "stop"].includes(n) ? (this.pendingConfirmation = null, { message: "Cancelled.", type: "success" }) : {
107
107
  message: "Please confirm: Yes or No.",
@@ -114,22 +114,25 @@ class B {
114
114
  }
115
115
  const i = this.commands.get(e);
116
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: {} }, {
117
+ const n = i;
118
+ if (n.macro)
119
+ return this.safeRunAction(n, {});
120
+ const a = (n.parameters ?? []).filter((o) => o.required);
121
+ return a.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
119
122
  message: `Please provide the required details for "${n.command}".`,
120
123
  type: "form",
121
- fields: o
124
+ fields: a
122
125
  }) : n.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(n), params: {} }, this.buildConfirmResponse(n)) : this.safeRunAction(n, {});
123
126
  }
124
127
  const s = await this.tryDeterministicMatch(e);
125
128
  if (s) return s;
126
129
  if (this.enableSmartIntent && this.openAIService) {
127
- const n = await this.getCommandsForAI(), o = await this.openAIService.determineIntent(
130
+ const n = await this.getCommandsForAI(), a = await this.openAIService.determineIntent(
128
131
  e,
129
132
  n,
130
133
  this.context
131
134
  );
132
- return this.handleAIResult(o);
135
+ return this.handleAIResult(a);
133
136
  }
134
137
  return this.enableSmartIntent ? this.listAllCommands() : { message: "I'm not sure what you mean.", type: "error" };
135
138
  }
@@ -138,10 +141,12 @@ class B {
138
141
  const e = this.getCommandById(t.match);
139
142
  if (!e)
140
143
  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 ");
144
+ if (e.macro)
145
+ return this.safeRunAction(e, {});
146
+ const i = t.params ?? {}, s = this.sanitizeParamsForCommand(e, i), n = e.allowAiParamExtraction === !1 ? {} : s, o = (e.parameters ?? []).filter((u) => u.required).filter((u) => n[u.name] == null || n[u.name] === "");
147
+ if (t.incomplete || o.length > 0) {
148
+ if (this.context = { commandId: this.getCommandIdentifier(e), params: n }, !(e.collectRequiredViaForm !== !1) && this.shouldAskSingleQuestion(o)) {
149
+ const p = o.map((g) => g.name).join(" and ");
145
150
  return {
146
151
  message: t.message || `Please provide ${p}.`,
147
152
  type: "question"
@@ -150,7 +155,7 @@ class B {
150
155
  return {
151
156
  message: t.message || `Please fill in the missing details for "${e.command}".`,
152
157
  type: "form",
153
- fields: r
158
+ fields: o
154
159
  };
155
160
  }
156
161
  if (e.critical)
@@ -180,52 +185,52 @@ class B {
180
185
  delete i[s.name];
181
186
  continue;
182
187
  }
183
- const o = n.trim();
184
- if (!o) {
188
+ const a = n.trim();
189
+ if (!a) {
185
190
  delete i[s.name];
186
191
  continue;
187
192
  }
188
- i[s.name] = o;
193
+ i[s.name] = a;
189
194
  }
190
195
  if (s.type === "number") {
191
- const o = typeof n == "number" ? n : Number(n == null ? void 0 : n.toString().trim());
192
- if (Number.isNaN(o)) {
196
+ const a = typeof n == "number" ? n : Number(n == null ? void 0 : n.toString().trim());
197
+ if (Number.isNaN(a)) {
193
198
  delete i[s.name];
194
199
  continue;
195
200
  }
196
- if (typeof s.min == "number" && o < s.min) {
201
+ if (typeof s.min == "number" && a < s.min) {
197
202
  delete i[s.name];
198
203
  continue;
199
204
  }
200
- if (typeof s.max == "number" && o > s.max) {
205
+ if (typeof s.max == "number" && a > s.max) {
201
206
  delete i[s.name];
202
207
  continue;
203
208
  }
204
- i[s.name] = o;
209
+ i[s.name] = a;
205
210
  }
206
211
  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];
212
+ const a = i[s.name];
213
+ if (typeof a == "string") {
214
+ const o = a.trim();
215
+ this.isIsoDateString(o) ? i[s.name] = o : delete i[s.name];
211
216
  } else
212
217
  delete i[s.name];
213
218
  }
214
219
  if (s.type === "select") {
215
- const o = typeof n == "string" ? n : n == null ? void 0 : n.toString();
216
- if (!o) {
220
+ const a = typeof n == "string" ? n : n == null ? void 0 : n.toString();
221
+ if (!a) {
217
222
  delete i[s.name];
218
223
  continue;
219
224
  }
220
- if (Array.isArray(s.options) && s.options.length > 0 && !s.options.some((c) => String(c.value) === String(o))) {
225
+ if (Array.isArray(s.options) && s.options.length > 0 && !s.options.some((c) => String(c.value) === String(a))) {
221
226
  delete i[s.name];
222
227
  continue;
223
228
  }
224
- i[s.name] = o;
229
+ i[s.name] = a;
225
230
  }
226
231
  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];
232
+ const a = i[s.name], o = a && typeof a == "object" && typeof a.name == "string" && typeof a.size == "number", c = typeof a == "string" && /^data:[^;]+;base64,/.test(a);
233
+ (s.delivery ?? "file") === "base64" ? !c && !o && delete i[s.name] : o || delete i[s.name];
229
234
  }
230
235
  }
231
236
  return i;
@@ -252,35 +257,35 @@ class B {
252
257
  }
253
258
  async tryDeterministicMatch(t) {
254
259
  const e = [];
255
- for (const r of this.commands.values()) {
260
+ for (const o of this.commands.values()) {
256
261
  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) {
262
+ const u = o.command.toLowerCase();
263
+ t.includes(u) && (c += 5);
264
+ const r = o.keywords ?? [];
265
+ for (const p of r) {
261
266
  const g = p.toLowerCase().trim();
262
267
  g && (t === g ? c += 4 : t.includes(g) && (c += 3));
263
268
  }
264
- c > 0 && e.push({ cmd: r, score: c });
269
+ c > 0 && e.push({ cmd: o, score: c });
265
270
  }
266
271
  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);
272
+ e.sort((o, c) => c.score - o.score);
273
+ const i = e[0].score, s = e.filter((o) => o.score === i).slice(0, 3);
269
274
  if (s.length > 1)
270
275
  return {
271
276
  message: "I think you mean one of these. Which one should I run?",
272
277
  type: "ambiguous",
273
- options: s.map((r) => ({
274
- label: r.cmd.command,
275
- value: r.cmd.command,
276
- commandId: r.cmd.id
278
+ options: s.map((o) => ({
279
+ label: o.cmd.command,
280
+ value: o.cmd.command,
281
+ commandId: o.cmd.id
277
282
  }))
278
283
  };
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: {} }, {
284
+ const n = s[0].cmd, a = (n.parameters ?? []).filter((o) => o.required);
285
+ return a.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
281
286
  message: `Please provide the required details for "${n.command}".`,
282
287
  type: "form",
283
- fields: o
288
+ fields: a
284
289
  }) : n.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(n), params: {} }, this.buildConfirmResponse(n)) : this.safeRunAction(n, {});
285
290
  }
286
291
  async safeRunAction(t, e) {
@@ -301,14 +306,14 @@ class B {
301
306
  e.parameters && await Promise.all(
302
307
  e.parameters.map(async (i) => {
303
308
  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) {
309
+ const s = `${e.id ?? e.command}:${i.name}`, n = this.selectOptionsCache.get(s), a = Date.now();
310
+ if (n && a - n.ts < 6e4) {
306
311
  i.options = n.options;
307
312
  return;
308
313
  }
309
314
  try {
310
- const r = await i.getOptions();
311
- this.selectOptionsCache.set(s, { options: r, ts: o }), i.options = r;
315
+ const o = await i.getOptions();
316
+ this.selectOptionsCache.set(s, { options: o, ts: a }), i.options = o;
312
317
  } catch {
313
318
  }
314
319
  })
@@ -345,7 +350,7 @@ class B {
345
350
  return Array.from(this.commands.keys());
346
351
  }
347
352
  }
348
- class H {
353
+ class P {
349
354
  constructor() {
350
355
  this.synth = typeof window < "u" ? window.speechSynthesis : null;
351
356
  }
@@ -375,18 +380,18 @@ class z {
375
380
  this.fallbackMessage = t;
376
381
  }
377
382
  handleFallback(t) {
378
- t && console.log(`Fallback triggered for: "${t}"`), console.log(this.fallbackMessage), new H().speak(this.fallbackMessage);
383
+ t && console.log(`Fallback triggered for: "${t}"`), console.log(this.fallbackMessage), new P().speak(this.fallbackMessage);
379
384
  }
380
385
  getFallbackMessage() {
381
386
  return this.fallbackMessage;
382
387
  }
383
388
  }
384
- const T = () => {
389
+ const $ = () => {
385
390
  if (typeof window > "u") return null;
386
391
  const d = window;
387
392
  return d.SpeechRecognition ?? d.webkitSpeechRecognition ?? null;
388
393
  };
389
- class O {
394
+ class W {
390
395
  constructor(t = "en-US", e = {}) {
391
396
  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 = () => {
392
397
  var s;
@@ -399,7 +404,7 @@ class O {
399
404
  }, this.onTTSEnd = () => {
400
405
  this.ttsSpeaking = !1, this.isListening && this.restartAllowed ? this.safeRestart() : this.emitStatus(this.isListening ? "listening" : "idle");
401
406
  };
402
- const i = T();
407
+ const i = $();
403
408
  if (i) {
404
409
  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 = () => {
405
410
  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");
@@ -431,7 +436,7 @@ class O {
431
436
  }
432
437
  /** Check if SpeechRecognition is available */
433
438
  isSupported() {
434
- return T() !== null;
439
+ return $() !== null;
435
440
  }
436
441
  /** Allow consumers (wrappers) to observe status changes */
437
442
  onStatusChange(t) {
@@ -469,11 +474,11 @@ class O {
469
474
  var s, n;
470
475
  if (!this.resultCallback) return;
471
476
  const i = e.confidenceThreshold ?? 0.6;
472
- for (let o = t.resultIndex; o < t.results.length; o++) {
473
- 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;
474
- if (f && !(!r.isFinal && e.interimResults === !1) && !(r.isFinal && a < i))
477
+ for (let a = t.resultIndex; a < t.results.length; a++) {
478
+ const o = t.results[a], c = o && o[0], u = ((n = (s = c == null ? void 0 : c.transcript) == null ? void 0 : s.trim) == null ? void 0 : n.call(s)) || "", r = (c == null ? void 0 : c.confidence) ?? 0;
479
+ if (u && !(!o.isFinal && e.interimResults === !1) && !(o.isFinal && r < i))
475
480
  try {
476
- this.hadResultThisSession = !0, this.resultCallback(f, !!r.isFinal);
481
+ this.hadResultThisSession = !0, this.resultCallback(u, !!o.isFinal);
477
482
  } catch {
478
483
  this.error("VoiceProcessor: result callback error");
479
484
  }
@@ -552,27 +557,29 @@ class O {
552
557
  }
553
558
  }
554
559
  }
555
- class U {
560
+ class R {
556
561
  constructor() {
557
- this.lastTap = 0;
562
+ this.lastTap = 0, this.tapCount = 0;
558
563
  }
559
564
  /**
560
- * Sets up double-click and double-tap listeners
561
- * @param onDoubleClickOrTap Callback to execute when a double-click or double-tap is detected
565
+ * Sets up triple-click and triple-tap listeners
566
+ * @param onTripleClickOrTap Callback to execute when a triple-click or triple-tap is detected
562
567
  */
563
- setupDoubleTapListener(t) {
564
- this.destroy(), this.dblClickListener = () => {
565
- t();
566
- }, document.addEventListener("dblclick", this.dblClickListener), this.touchEndListener = () => {
568
+ setupTripleTapListener(t) {
569
+ this.destroy(), this.clickListener = () => {
570
+ this.tapCount++, this.tapCount === 1 ? this.tapTimeout = window.setTimeout(() => {
571
+ this.tapCount = 0;
572
+ }, 500) : this.tapCount === 3 && (clearTimeout(this.tapTimeout), this.tapCount = 0, t());
573
+ }, document.addEventListener("click", this.clickListener), this.touchEndListener = () => {
567
574
  const e = (/* @__PURE__ */ new Date()).getTime(), i = e - this.lastTap;
568
- i < 300 && i > 0 && t(), this.lastTap = e;
575
+ i < 500 && i > 0 ? (this.tapCount++, this.tapCount === 3 && (this.tapCount = 0, t())) : this.tapCount = 1, this.lastTap = e;
569
576
  }, document.addEventListener("touchend", this.touchEndListener);
570
577
  }
571
578
  destroy() {
572
- this.dblClickListener && document.removeEventListener("dblclick", this.dblClickListener), this.touchEndListener && document.removeEventListener("touchend", this.touchEndListener), this.dblClickListener = void 0, this.touchEndListener = void 0;
579
+ this.dblClickListener && document.removeEventListener("dblclick", this.dblClickListener), this.touchEndListener && document.removeEventListener("touchend", this.touchEndListener), this.clickListener && document.removeEventListener("click", this.clickListener), this.tapTimeout && clearTimeout(this.tapTimeout), this.dblClickListener = void 0, this.touchEndListener = void 0, this.clickListener = void 0, this.tapTimeout = void 0, this.tapCount = 0;
573
580
  }
574
581
  }
575
- function D() {
582
+ function j() {
576
583
  if (document.querySelector("#assistant-styles")) {
577
584
  console.log("Styles already injected");
578
585
  return;
@@ -613,20 +620,20 @@ function D() {
613
620
  }
614
621
  `, document.head.appendChild(t), console.log("Gradient styles injected");
615
622
  }
616
- function $() {
623
+ function U() {
617
624
  if (document.querySelector("#gradient-indicator"))
618
625
  return;
619
626
  const d = document.createElement("div");
620
- d.id = "gradient-indicator", D(), d.classList.add("gradient-indicator"), document.body.appendChild(d), console.log("Gradient indicator added to the DOM");
627
+ d.id = "gradient-indicator", j(), d.classList.add("gradient-indicator"), document.body.appendChild(d), console.log("Gradient indicator added to the DOM");
621
628
  }
622
- function j() {
629
+ function D() {
623
630
  const d = document.querySelector("#gradient-indicator");
624
631
  d && (d.remove(), console.log("Gradient indicator removed from the DOM"));
625
632
  }
626
633
  function V() {
627
634
  return typeof window < "u" && typeof document < "u";
628
635
  }
629
- class G {
636
+ class _ {
630
637
  constructor() {
631
638
  this.state = "idle", this.subscribers = [];
632
639
  }
@@ -634,7 +641,7 @@ class G {
634
641
  return this.state;
635
642
  }
636
643
  setState(t) {
637
- this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? $() : j();
644
+ this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? U() : D();
638
645
  }
639
646
  // eslint-disable-next-line no-unused-vars
640
647
  subscribe(t) {
@@ -644,9 +651,81 @@ class G {
644
651
  this.subscribers.forEach((t) => t(this.state));
645
652
  }
646
653
  }
647
- class Y {
654
+ class G {
648
655
  constructor(t) {
649
- this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1, this.loadingEl = null, this.active = V(), this.config = t, this.active && this.init();
656
+ this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1, this.loadingEl = null, this.commandHandlers = /* @__PURE__ */ new Map(), this.active = V(), this.handleClickOutside = (e) => {
657
+ const i = e.target;
658
+ this.chatWindow && this.chatWindow.contains(i) || i.closest(".foisit-floating-btn") || this.toggle();
659
+ }, this.config = t, this.active && this.init();
660
+ }
661
+ /** Register a command handler that can be invoked programmatically via `runCommand` */
662
+ registerCommandHandler(t, e) {
663
+ !t || typeof e != "function" || this.commandHandlers.set(t, e);
664
+ }
665
+ /** Check whether a programmatic handler is registered locally */
666
+ hasCommandHandler(t) {
667
+ return this.commandHandlers.has(t);
668
+ }
669
+ /** Set an external executor (used to run commands registered via CommandHandler) */
670
+ setExternalCommandExecutor(t) {
671
+ this.externalCommandExecutor = t;
672
+ }
673
+ /** Unregister a previously registered command handler */
674
+ unregisterCommandHandler(t) {
675
+ t && this.commandHandlers.delete(t);
676
+ }
677
+ /**
678
+ * Run a registered command by id. Options:
679
+ * - `params`: passed to the handler
680
+ * - `openOverlay`: if true, open the overlay before running
681
+ * - `showInvocation`: if true, show the invocation as a user message
682
+ */
683
+ async runCommand(t) {
684
+ if (!t || !t.commandId)
685
+ throw new Error("runCommand requires a commandId");
686
+ const {
687
+ commandId: e,
688
+ params: i,
689
+ openOverlay: s = !0,
690
+ showInvocation: n = !0
691
+ } = t;
692
+ s && !this.isOpen && this.toggle();
693
+ const a = this.commandHandlers.get(e);
694
+ if (a && n && this.messagesContainer && this.addMessage(`Command: ${e}`, "user"), a)
695
+ try {
696
+ this.showLoading();
697
+ const o = await a(i);
698
+ if (this.hideLoading(), typeof o == "string")
699
+ this.addMessage(o, "system");
700
+ else if (o && typeof o == "object")
701
+ try {
702
+ this.addMessage(JSON.stringify(o, null, 2), "system");
703
+ } catch {
704
+ this.addMessage(String(o), "system");
705
+ }
706
+ else o == null || this.addMessage(String(o), "system");
707
+ return o;
708
+ } catch (o) {
709
+ throw this.hideLoading(), this.addMessage(
710
+ `Command "${e}" failed: ${String(o)}`,
711
+ "system"
712
+ ), o;
713
+ }
714
+ if (this.externalCommandExecutor)
715
+ try {
716
+ this.showLoading();
717
+ const o = await this.externalCommandExecutor({ commandId: e, params: i });
718
+ return this.hideLoading(), o;
719
+ } catch (o) {
720
+ throw this.hideLoading(), this.addMessage(
721
+ `Command "${e}" failed: ${String(o)}`,
722
+ "system"
723
+ ), o;
724
+ }
725
+ this.addMessage(
726
+ `No handler registered for command "${e}".`,
727
+ "system"
728
+ );
650
729
  }
651
730
  init() {
652
731
  var e, i;
@@ -654,17 +733,23 @@ class Y {
654
733
  this.injectOverlayStyles();
655
734
  const t = document.getElementById("foisit-overlay-container");
656
735
  if (t && t instanceof HTMLElement) {
657
- 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();
736
+ this.container = t, this.chatWindow = t.querySelector(
737
+ ".foisit-chat"
738
+ ), this.messagesContainer = t.querySelector(
739
+ ".foisit-messages"
740
+ ), this.input = t.querySelector(
741
+ "input.foisit-input"
742
+ ), ((e = this.config.floatingButton) == null ? void 0 : e.visible) !== !1 && !t.querySelector(".foisit-floating-btn") && this.renderFloatingButton(), this.chatWindow || this.renderChatWindow(), this.config.enableGestureActivation && (this.gestureHandler = new R(), this.gestureHandler.setupTripleTapListener(() => this.toggle()));
658
743
  return;
659
744
  }
660
- 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();
745
+ 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(), this.config.enableGestureActivation && (this.gestureHandler = new R(), this.gestureHandler.setupTripleTapListener(() => this.toggle()));
661
746
  }
662
747
  renderFloatingButton() {
663
- var s, n, o, r, c, f;
748
+ var s, n, a, o, c, u;
664
749
  const t = document.createElement("button");
665
750
  t.innerHTML = ((s = this.config.floatingButton) == null ? void 0 : s.customHtml) || "🎙️";
666
- 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";
667
- 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);
751
+ const e = ((a = (n = this.config.floatingButton) == null ? void 0 : n.position) == null ? void 0 : a.bottom) || "20px", i = ((c = (o = this.config.floatingButton) == null ? void 0 : o.position) == null ? void 0 : c.right) || "20px";
752
+ 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)", (u = this.container) == null || u.appendChild(t);
668
753
  }
669
754
  renderChatWindow() {
670
755
  var n;
@@ -677,9 +762,9 @@ class Y {
677
762
  const i = document.createElement("button");
678
763
  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";
679
764
  const s = document.createElement("div");
680
- 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) => {
681
- var r;
682
- if (o.key === "Enter" && ((r = this.input) != null && r.value.trim())) {
765
+ 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", (a) => {
766
+ var o;
767
+ if (a.key === "Enter" && ((o = this.input) != null && o.value.trim())) {
683
768
  const c = this.input.value.trim();
684
769
  this.input.value = "", this.onSubmit && this.onSubmit(c);
685
770
  }
@@ -689,19 +774,27 @@ class Y {
689
774
  this.active && (this.onSubmit = t, this.onClose = e);
690
775
  }
691
776
  toggle(t, e) {
692
- this.active && (t && (this.onSubmit = t), e && (this.onClose = e), this.isOpen = !this.isOpen, this.chatWindow && (this.isOpen ? (this.chatWindow.style.display = "flex", requestAnimationFrame(() => {
777
+ this.active && (t && (this.onSubmit = t), e && (this.onClose = e), this.isOpen = !this.isOpen, this.chatWindow && (this.isOpen ? (this.container && (this.container.style.pointerEvents = "auto"), this.chatWindow.style.display = "flex", requestAnimationFrame(() => {
693
778
  this.chatWindow && (this.chatWindow.style.opacity = "1", this.chatWindow.style.transform = "translateY(0) scale(1)");
694
779
  }), setTimeout(() => {
695
780
  var i;
696
781
  return (i = this.input) == null ? void 0 : i.focus();
697
- }, 100)) : (this.chatWindow.style.opacity = "0", this.chatWindow.style.transform = "translateY(20px) scale(0.95)", setTimeout(() => {
782
+ }, 100), this.addClickOutsideListener()) : (this.chatWindow.style.opacity = "0", this.chatWindow.style.transform = "translateY(20px) scale(0.95)", setTimeout(() => {
698
783
  this.chatWindow && !this.isOpen && (this.chatWindow.style.display = "none");
699
- }, 200), this.onClose && this.onClose())));
784
+ }, 200), this.onClose && this.onClose(), this.removeClickOutsideListener(), this.container && (this.container.style.pointerEvents = "none"))));
785
+ }
786
+ addClickOutsideListener() {
787
+ this.container && (this.removeClickOutsideListener(), this.container.addEventListener("click", this.handleClickOutside));
788
+ }
789
+ removeClickOutsideListener() {
790
+ this.container && this.container.removeEventListener("click", this.handleClickOutside);
700
791
  }
701
792
  addMessage(t, e) {
702
793
  if (!this.messagesContainer) return;
703
794
  const i = document.createElement("div");
704
- i.textContent = t, i.className = e === "user" ? "foisit-bubble user" : "foisit-bubble system", this.messagesContainer.appendChild(i), this.scrollToBottom();
795
+ e === "system" ? i.innerHTML = this.renderMarkdown(t) : i.textContent = t, i.className = e === "user" ? "foisit-bubble user" : "foisit-bubble system";
796
+ const s = (t || "").length || 0, n = Math.max(120, 700 - Math.min(600, Math.floor(s * 6)));
797
+ i.style.opacity = "0", i.style.transform = "translateY(8px)", i.style.transition = "none", this.messagesContainer.appendChild(i), this.animateMessageEntrance(i, n), this.scrollToBottom();
705
798
  }
706
799
  addOptions(t) {
707
800
  if (!this.messagesContainer) return;
@@ -714,11 +807,11 @@ class Y {
714
807
  this.onSubmit && this.onSubmit({ commandId: i.commandId });
715
808
  return;
716
809
  }
717
- const o = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
718
- this.onSubmit && this.onSubmit(o);
810
+ const a = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
811
+ this.onSubmit && this.onSubmit(a);
719
812
  };
720
- s.onclick = n, s.onkeydown = (o) => {
721
- (o.key === "Enter" || o.key === " ") && (o.preventDefault(), n());
813
+ s.onclick = n, s.onkeydown = (a) => {
814
+ (a.key === "Enter" || a.key === " ") && (a.preventDefault(), n());
722
815
  }, e.appendChild(s);
723
816
  }), this.messagesContainer.appendChild(e), this.scrollToBottom();
724
817
  }
@@ -727,89 +820,95 @@ class Y {
727
820
  this.addMessage(t, "system");
728
821
  const s = document.createElement("form");
729
822
  s.className = "foisit-form";
730
- const n = [], o = (a, p) => {
823
+ const n = [], a = (r, p) => {
731
824
  const g = document.createElement("div");
732
- return g.className = "foisit-form-label", g.innerHTML = a + (p ? ' <span class="foisit-req-star">*</span>' : ""), g;
733
- }, r = () => {
734
- const a = document.createElement("div");
735
- return a.className = "foisit-form-error", a.style.display = "none", a;
825
+ return g.className = "foisit-form-label", g.innerHTML = r + (p ? ' <span class="foisit-req-star">*</span>' : ""), g;
826
+ }, o = () => {
827
+ const r = document.createElement("div");
828
+ return r.className = "foisit-form-error", r.style.display = "none", r;
736
829
  };
737
- (e ?? []).forEach((a) => {
830
+ (e ?? []).forEach((r) => {
738
831
  const p = document.createElement("div");
739
832
  p.className = "foisit-form-group";
740
- const g = a.description || a.name;
741
- p.appendChild(o(g, a.required));
833
+ const g = r.description || r.name;
834
+ p.appendChild(a(g, r.required));
742
835
  let m;
743
- if (a.type === "select") {
836
+ if (r.type === "select") {
744
837
  const l = document.createElement("select");
745
838
  l.className = "foisit-form-input";
746
- const b = document.createElement("option");
747
- b.value = "", b.textContent = "Select...", l.appendChild(b);
748
- const w = (h) => {
749
- (h ?? []).forEach((x) => {
750
- const y = document.createElement("option");
751
- y.value = String(x.value ?? x.label ?? ""), y.textContent = String(x.label ?? x.value ?? ""), l.appendChild(y);
839
+ const y = document.createElement("option");
840
+ y.value = "", y.textContent = "Select...", l.appendChild(y);
841
+ const x = (h) => {
842
+ (h ?? []).forEach((w) => {
843
+ const b = document.createElement("option");
844
+ b.value = String(w.value ?? w.label ?? ""), b.textContent = String(w.label ?? w.value ?? ""), l.appendChild(b);
752
845
  });
753
846
  };
754
- if (Array.isArray(a.options) && a.options.length)
755
- w(a.options);
756
- else if (typeof a.getOptions == "function") {
757
- const h = a.getOptions, x = document.createElement("option");
758
- x.value = "", x.textContent = "Loading...", l.appendChild(x), Promise.resolve().then(() => h()).then((y) => {
847
+ if (Array.isArray(r.options) && r.options.length)
848
+ x(r.options);
849
+ else if (typeof r.getOptions == "function") {
850
+ const h = r.getOptions, w = document.createElement("option");
851
+ w.value = "", w.textContent = "Loading...", l.appendChild(w), Promise.resolve().then(() => h()).then((b) => {
759
852
  for (; l.options.length > 1; ) l.remove(1);
760
- w(y);
853
+ x(b);
761
854
  }).catch(() => {
762
855
  for (; l.options.length > 1; ) l.remove(1);
763
- const y = document.createElement("option");
764
- y.value = "", y.textContent = "Error loading options", l.appendChild(y);
856
+ const b = document.createElement("option");
857
+ b.value = "", b.textContent = "Error loading options", l.appendChild(b);
765
858
  });
766
859
  }
767
- a.defaultValue != null && (l.value = String(a.defaultValue)), m = l;
768
- } else if (a.type === "file") {
769
- const l = a, b = document.createElement("input");
770
- 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 () => {
771
- const w = Array.from(b.files || []), h = C;
772
- if (h.style.display = "none", h.textContent = "", w.length === 0) return;
773
- const x = l.maxFiles ?? (l.multiple ? 10 : 1);
774
- if (w.length > x) {
775
- h.textContent = `Please select at most ${x} file(s).`, h.style.display = "block";
860
+ r.defaultValue != null && (l.value = String(r.defaultValue)), m = l;
861
+ } else if (r.type === "file") {
862
+ const l = r, y = document.createElement("input");
863
+ y.className = "foisit-form-input", y.type = "file", l.accept && Array.isArray(l.accept) && (y.accept = l.accept.join(",")), l.multiple && (y.multiple = !0), l.capture && (l.capture === !0 ? y.setAttribute("capture", "") : y.setAttribute("capture", String(l.capture))), y.addEventListener("change", async () => {
864
+ const x = Array.from(y.files || []), h = C;
865
+ if (h.style.display = "none", h.textContent = "", x.length === 0) return;
866
+ const w = l.maxFiles ?? (l.multiple ? 10 : 1);
867
+ if (x.length > w) {
868
+ h.textContent = `Please select at most ${w} file(s).`, h.style.display = "block";
776
869
  return;
777
870
  }
778
- const y = l.maxSizeBytes ?? 1 / 0, u = w.reduce((v, A) => v + A.size, 0);
779
- if (w.some((v) => v.size > y)) {
780
- h.textContent = `One or more files exceed the maximum size of ${Math.round(y / 1024)} KB.`, h.style.display = "block";
871
+ const b = l.maxSizeBytes ?? 1 / 0, f = x.reduce((v, E) => v + E.size, 0);
872
+ if (x.some((v) => v.size > b)) {
873
+ h.textContent = `One or more files exceed the maximum size of ${Math.round(
874
+ b / 1024
875
+ )} KB.`, h.style.display = "block";
781
876
  return;
782
877
  }
783
878
  const S = l.maxTotalBytes ?? 1 / 0;
784
- if (u > S) {
785
- h.textContent = `Total selected files exceed the maximum of ${Math.round(S / 1024)} KB.`, h.style.display = "block";
879
+ if (f > S) {
880
+ h.textContent = `Total selected files exceed the maximum of ${Math.round(
881
+ S / 1024
882
+ )} KB.`, h.style.display = "block";
786
883
  return;
787
884
  }
788
885
  if (l.accept && Array.isArray(l.accept)) {
789
886
  const v = l.accept;
790
- if (!w.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)) {
887
+ if (!x.every((L) => L.type ? v.some(
888
+ (A) => A.startsWith(".") ? L.name.toLowerCase().endsWith(A.toLowerCase()) : L.type === A || L.type.startsWith(A.split("/")[0] + "/")
889
+ ) : !0)) {
791
890
  h.textContent = "One or more files have an unsupported type.", h.style.display = "block";
792
891
  return;
793
892
  }
794
893
  }
795
- }), m = b;
894
+ }), m = y;
796
895
  } else {
797
896
  const l = document.createElement("input");
798
- 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;
897
+ l.className = "foisit-form-input", r.type === "string" && (l.placeholder = r.placeholder || "Type here..."), r.type === "number" ? (l.type = "number", typeof r.min == "number" && (l.min = String(r.min)), typeof r.max == "number" && (l.max = String(r.max)), typeof r.step == "number" && (l.step = String(r.step)), r.defaultValue != null && (l.value = String(r.defaultValue))) : r.type === "date" ? (l.type = "date", typeof r.min == "string" && (l.min = r.min), typeof r.max == "string" && (l.max = r.max), r.defaultValue != null && (l.value = String(r.defaultValue))) : (l.type = "text", r.defaultValue != null && (l.value = String(r.defaultValue))), m = l;
799
898
  }
800
- const C = r();
899
+ const C = o();
801
900
  p.appendChild(m), p.appendChild(C), n.push({
802
- name: a.name,
803
- type: a.type,
901
+ name: r.name,
902
+ type: r.type,
804
903
  el: m,
805
- required: a.required
904
+ required: r.required
806
905
  }), s.appendChild(p);
807
906
  });
808
907
  const c = document.createElement("div");
809
908
  c.className = "foisit-form-actions";
810
- const f = document.createElement("button");
811
- 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) => {
812
- a.preventDefault();
909
+ const u = document.createElement("button");
910
+ u.type = "submit", u.textContent = "Submit", u.className = "foisit-option-chip", u.style.fontWeight = "600", c.appendChild(u), s.appendChild(c), s.onsubmit = async (r) => {
911
+ r.preventDefault();
813
912
  const p = {};
814
913
  let g = !1;
815
914
  s.querySelectorAll(".foisit-form-error").forEach((m) => {
@@ -819,56 +918,62 @@ class Y {
819
918
  });
820
919
  for (const m of n) {
821
920
  if (m.type === "file") {
822
- const w = m.el.parentElement, h = w == null ? void 0 : w.querySelector(".foisit-form-error"), x = m.el, y = Array.from(x.files || []);
823
- if (m.required && y.length === 0) {
824
- g = !0, x.classList.add("foisit-error-border"), h && (h.textContent = "This file is required", h.style.display = "block");
921
+ const x = m.el.parentElement, h = x == null ? void 0 : x.querySelector(
922
+ ".foisit-form-error"
923
+ ), w = m.el, b = Array.from(w.files || []);
924
+ if (m.required && b.length === 0) {
925
+ g = !0, w.classList.add("foisit-error-border"), h && (h.textContent = "This file is required", h.style.display = "block");
825
926
  continue;
826
927
  }
827
- if (y.length === 0) continue;
828
- const u = (e ?? []).find((v) => v.name === m.name), S = (u == null ? void 0 : u.delivery) ?? "file";
829
- if (u != null && u.maxWidth || u != null && u.maxHeight)
928
+ if (b.length === 0) continue;
929
+ const f = (e ?? []).find((v) => v.name === m.name), S = (f == null ? void 0 : f.delivery) ?? "file";
930
+ if (f != null && f.maxWidth || f != null && f.maxHeight)
830
931
  try {
831
- const v = await this.getImageDimensions(y[0]);
832
- if (u.maxWidth && v.width > u.maxWidth) {
833
- g = !0, h && (h.textContent = `Image width must be ≤ ${u.maxWidth}px`, h.style.display = "block");
932
+ const v = await this.getImageDimensions(b[0]);
933
+ if (f.maxWidth && v.width > f.maxWidth) {
934
+ g = !0, h && (h.textContent = `Image width must be ≤ ${f.maxWidth}px`, h.style.display = "block");
834
935
  continue;
835
936
  }
836
- if (u.maxHeight && v.height > u.maxHeight) {
837
- g = !0, h && (h.textContent = `Image height must be ≤ ${u.maxHeight}px`, h.style.display = "block");
937
+ if (f.maxHeight && v.height > f.maxHeight) {
938
+ g = !0, h && (h.textContent = `Image height must be ≤ ${f.maxHeight}px`, h.style.display = "block");
838
939
  continue;
839
940
  }
840
941
  } catch {
841
942
  }
842
- if (u != null && u.maxDurationSec)
943
+ if (f != null && f.maxDurationSec)
843
944
  try {
844
- const v = await this.getMediaDuration(y[0]);
845
- if (v && v > u.maxDurationSec) {
846
- g = !0, h && (h.textContent = `Media duration must be ≤ ${u.maxDurationSec}s`, h.style.display = "block");
945
+ const v = await this.getMediaDuration(b[0]);
946
+ if (v && v > f.maxDurationSec) {
947
+ g = !0, h && (h.textContent = `Media duration must be ≤ ${f.maxDurationSec}s`, h.style.display = "block");
847
948
  continue;
848
949
  }
849
950
  } catch {
850
951
  }
851
952
  if (S === "file")
852
- p[m.name] = u != null && u.multiple ? y : y[0];
953
+ p[m.name] = f != null && f.multiple ? b : b[0];
853
954
  else if (S === "base64")
854
955
  try {
855
- const v = await Promise.all(y.map((A) => this.readFileAsDataURL(A)));
856
- p[m.name] = u != null && u.multiple ? v : v[0];
956
+ const v = await Promise.all(
957
+ b.map((E) => this.readFileAsDataURL(E))
958
+ );
959
+ p[m.name] = f != null && f.multiple ? v : v[0];
857
960
  } catch {
858
961
  g = !0, h && (h.textContent = "Failed to encode file(s) to base64.", h.style.display = "block");
859
962
  continue;
860
963
  }
861
964
  continue;
862
965
  }
863
- const C = (m.el.value ?? "").toString().trim(), l = m.el.parentElement, b = l == null ? void 0 : l.querySelector(".foisit-form-error");
966
+ const C = (m.el.value ?? "").toString().trim(), l = m.el.parentElement, y = l == null ? void 0 : l.querySelector(
967
+ ".foisit-form-error"
968
+ );
864
969
  if (m.required && (C == null || C === "")) {
865
- g = !0, m.el.classList.add("foisit-error-border"), b && (b.textContent = "This field is required", b.style.display = "block");
970
+ g = !0, m.el.classList.add("foisit-error-border"), y && (y.textContent = "This field is required", y.style.display = "block");
866
971
  continue;
867
972
  }
868
973
  if (C !== "")
869
974
  if (m.type === "number") {
870
- const w = Number(C);
871
- Number.isNaN(w) || (p[m.name] = w);
975
+ const x = Number(C);
976
+ Number.isNaN(x) || (p[m.name] = x);
872
977
  } else
873
978
  p[m.name] = C;
874
979
  }
@@ -876,7 +981,7 @@ class Y {
876
981
  s.classList.add("foisit-shake"), setTimeout(() => s.classList.remove("foisit-shake"), 400);
877
982
  return;
878
983
  }
879
- f.disabled = !0, f.style.opacity = "0.6", n.forEach((m) => {
984
+ u.disabled = !0, u.style.opacity = "0.6", n.forEach((m) => {
880
985
  m.el.disabled = !0;
881
986
  }), i(p);
882
987
  }, this.messagesContainer.appendChild(s), this.scrollToBottom();
@@ -898,9 +1003,72 @@ class Y {
898
1003
  scrollToBottom() {
899
1004
  this.messagesContainer && (this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight);
900
1005
  }
1006
+ /** Subtle entrance animation for new messages */
1007
+ animateMessageEntrance(t, e) {
1008
+ if (!t) return;
1009
+ t.style.transition = `opacity ${e}ms cubic-bezier(0.22, 0.9, 0.32, 1), transform ${Math.max(
1010
+ 120,
1011
+ e
1012
+ )}ms cubic-bezier(0.22, 0.9, 0.32, 1)`, requestAnimationFrame(() => {
1013
+ t.style.opacity = "1", t.style.transform = "translateY(0)";
1014
+ });
1015
+ const i = () => {
1016
+ try {
1017
+ t.style.transition = "";
1018
+ } catch {
1019
+ }
1020
+ t.removeEventListener("transitionend", i);
1021
+ };
1022
+ t.addEventListener("transitionend", i);
1023
+ }
1024
+ /** Smoothly scroll messages container to bottom over duration (ms) */
1025
+ animateScrollToBottom(t) {
1026
+ if (!this.messagesContainer) return;
1027
+ const e = this.messagesContainer, i = e.scrollTop, s = e.scrollHeight - e.clientHeight;
1028
+ if (s <= i || t <= 0) {
1029
+ e.scrollTop = s;
1030
+ return;
1031
+ }
1032
+ const n = s - i, a = performance.now(), o = (c) => {
1033
+ const u = Math.min(1, (c - a) / t), r = 1 - Math.pow(1 - u, 3);
1034
+ e.scrollTop = Math.round(i + n * r), u < 1 && requestAnimationFrame(o);
1035
+ };
1036
+ requestAnimationFrame(o);
1037
+ }
901
1038
  destroy() {
902
1039
  var t;
903
- (t = this.container) == null || t.remove(), this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1;
1040
+ this.removeClickOutsideListener(), (t = this.container) == null || t.remove(), this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1;
1041
+ }
1042
+ /** Escape HTML special characters to prevent XSS */
1043
+ escapeHtml(t) {
1044
+ const e = {
1045
+ "&": "&amp;",
1046
+ "<": "&lt;",
1047
+ ">": "&gt;",
1048
+ '"': "&quot;",
1049
+ "'": "&#039;"
1050
+ };
1051
+ return t.replace(/[&<>"']/g, (i) => e[i]);
1052
+ }
1053
+ /** Simple markdown renderer for AI responses */
1054
+ renderMarkdown(t) {
1055
+ let e = this.escapeHtml(t);
1056
+ return e = e.replace(/```(\w*)\n([\s\S]*?)```/g, (i, s, n) => `<pre class="foisit-code-block"><code${s ? ` class="language-${s}"` : ""}>${n.trim()}</code></pre>`), e = e.replace(
1057
+ /`([^`]+)`/g,
1058
+ '<code class="foisit-inline-code">$1</code>'
1059
+ ), e = e.replace(/^###### (.+)$/gm, '<h6 class="foisit-md-h6">$1</h6>'), e = e.replace(/^##### (.+)$/gm, '<h5 class="foisit-md-h5">$1</h5>'), e = e.replace(/^#### (.+)$/gm, '<h4 class="foisit-md-h4">$1</h4>'), e = e.replace(/^### (.+)$/gm, '<h3 class="foisit-md-h3">$1</h3>'), e = e.replace(/^## (.+)$/gm, '<h2 class="foisit-md-h2">$1</h2>'), e = e.replace(/^# (.+)$/gm, '<h1 class="foisit-md-h1">$1</h1>'), e = e.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"), e = e.replace(/__([^_]+)__/g, "<strong>$1</strong>"), e = e.replace(/\*([^*]+)\*/g, "<em>$1</em>"), e = e.replace(new RegExp("(?<!_)_([^_]+)_(?!_)", "g"), "<em>$1</em>"), e = e.replace(/~~([^~]+)~~/g, "<del>$1</del>"), e = e.replace(
1060
+ /\[([^\]]+)\]\(([^)]+)\)/g,
1061
+ '<a href="$2" target="_blank" rel="noopener noreferrer" class="foisit-md-link">$1</a>'
1062
+ ), e = e.replace(/^[\-\*] (.+)$/gm, '<li class="foisit-md-li">$1</li>'), e = e.replace(
1063
+ /(<li class="foisit-md-li">.*<\/li>\n?)+/g,
1064
+ (i) => `<ul class="foisit-md-ul">${i}</ul>`
1065
+ ), e = e.replace(/^\d+\. (.+)$/gm, '<li class="foisit-md-li">$1</li>'), e = e.replace(
1066
+ new RegExp('(?<!<\\/ul>)(<li class="foisit-md-li">.*<\\/li>\\n?)+', "g"),
1067
+ (i) => i.includes("<ul") ? i : `<ol class="foisit-md-ol">${i}</ol>`
1068
+ ), e = e.replace(
1069
+ /^&gt; (.+)$/gm,
1070
+ '<blockquote class="foisit-md-blockquote">$1</blockquote>'
1071
+ ), e = e.replace(/^(---|___|\*\*\*)$/gm, '<hr class="foisit-md-hr">'), e = e.replace(/\n\n+/g, '</p><p class="foisit-md-p">'), e = e.replace(/\n/g, "<br>"), e.match(/^<(h[1-6]|ul|ol|pre|blockquote|hr|p)/) || (e = `<p class="foisit-md-p">${e}</p>`), e;
904
1072
  }
905
1073
  readFileAsDataURL(t) {
906
1074
  return new Promise((e, i) => {
@@ -913,7 +1081,10 @@ class Y {
913
1081
  try {
914
1082
  const i = URL.createObjectURL(t), s = new Image();
915
1083
  s.onload = () => {
916
- const n = { width: s.naturalWidth || s.width, height: s.naturalHeight || s.height };
1084
+ const n = {
1085
+ width: s.naturalWidth || s.width,
1086
+ height: s.naturalHeight || s.height
1087
+ };
917
1088
  URL.revokeObjectURL(i), e(n);
918
1089
  }, s.onerror = () => {
919
1090
  URL.revokeObjectURL(i), e({ width: 0, height: 0 });
@@ -928,16 +1099,16 @@ class Y {
928
1099
  try {
929
1100
  const i = URL.createObjectURL(t), s = t.type.startsWith("audio") ? document.createElement("audio") : document.createElement("video");
930
1101
  let n = !1;
931
- const o = setTimeout(() => {
1102
+ const a = setTimeout(() => {
932
1103
  n || (n = !0, URL.revokeObjectURL(i), e(0));
933
1104
  }, 5e3);
934
1105
  s.preload = "metadata", s.onloadedmetadata = () => {
935
1106
  if (n) return;
936
- n = !0, clearTimeout(o);
1107
+ n = !0, clearTimeout(a);
937
1108
  const c = s.duration || 0;
938
1109
  URL.revokeObjectURL(i), e(c);
939
1110
  }, s.onerror = () => {
940
- n || (n = !0, clearTimeout(o), URL.revokeObjectURL(i), e(0));
1111
+ n || (n = !0, clearTimeout(a), URL.revokeObjectURL(i), e(0));
941
1112
  }, s.src = i;
942
1113
  } catch {
943
1114
  e(0);
@@ -947,57 +1118,93 @@ class Y {
947
1118
  injectOverlayStyles() {
948
1119
  if (document.getElementById("foisit-overlay-styles")) return;
949
1120
  const t = document.createElement("style");
950
- t.id = "foisit-overlay-styles", t.textContent = `
951
- :root {
952
- /* LIGHT MODE (Default) - Smoother gradient */
953
- /* Changed: Softer, right-focused radial highlight to avoid a heavy white bottom */
954
- --foisit-bg: radial-gradient(ellipse at 75% 30%, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.03));
955
- --foisit-border: 1px solid rgba(255, 255, 255, 0.25);
956
- --foisit-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
957
- --foisit-text: #333;
958
-
959
- /* Input */
960
- --foisit-input-color: #333;
961
- --foisit-input-placeholder: rgba(60, 60, 67, 0.6);
1121
+ t.id = "foisit-overlay-styles";
1122
+ const e = this.config.theme || "glass", i = this.config.themeColors || {};
1123
+ if (e === "solid") {
1124
+ const s = i.background || "#1a1a2e", n = i.text || "#ffffff", a = i.accent || "linear-gradient(135deg, #667eea 0%, #764ba2 100%)", o = i.userBubbleBg || "rgba(102, 126, 234, 0.2)", c = i.systemBubbleBg || "rgba(255, 255, 255, 0.08)", u = i.border || "rgba(255, 255, 255, 0.1)";
1125
+ t.textContent = `
1126
+ :root {
1127
+ /* SOLID THEME - Custom Colors */
1128
+ --foisit-bg: ${s};
1129
+ --foisit-border: 1px solid ${u};
1130
+ --foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
1131
+ --foisit-text: ${n};
1132
+ --foisit-accent: ${a};
1133
+ --foisit-backdrop: none;
962
1134
 
963
- /* Bubbles */
964
- --foisit-bubble-user-bg: rgba(0, 0, 0, 0.04);
965
- --foisit-bubble-user-text: #333;
1135
+ /* Input */
1136
+ --foisit-input-color: ${n};
1137
+ --foisit-input-placeholder: ${n}99;
966
1138
 
967
- --foisit-bubble-sys-bg: rgba(255, 255, 255, 0.45);
968
- --foisit-bubble-sys-text: #333;
1139
+ /* Bubbles */
1140
+ --foisit-bubble-user-bg: ${o};
1141
+ --foisit-bubble-user-text: ${n};
969
1142
 
970
- /* Form Colors */
971
- --foisit-req-star: #ef4444; /* Red asterisk */
972
- --foisit-error-text: #dc2626;
973
- --foisit-error-border: #fca5a5;
974
- }
1143
+ --foisit-bubble-sys-bg: ${c};
1144
+ --foisit-bubble-sys-text: ${n}ee;
975
1145
 
976
- @media (prefers-color-scheme: dark) {
1146
+ /* Form Colors */
1147
+ --foisit-req-star: #f87171;
1148
+ --foisit-error-text: #fca5a5;
1149
+ --foisit-error-border: #f87171;
1150
+ }
1151
+ `;
1152
+ } else
1153
+ t.textContent = `
977
1154
  :root {
978
- /* DARK MODE */
979
- --foisit-bg: linear-gradient(135deg, rgba(40, 40, 40, 0.65), rgba(40, 40, 40, 0.25));
980
- --foisit-border: 1px solid rgba(255, 255, 255, 0.1);
981
- --foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
982
- --foisit-text: #fff;
1155
+ /* LIGHT MODE (Default) - Smoother gradient */
1156
+ /* Changed: Softer, right-focused radial highlight to avoid a heavy white bottom */
1157
+ --foisit-bg: radial-gradient(ellipse at 75% 30%, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.03));
1158
+ --foisit-border: 1px solid rgba(255, 255, 255, 0.25);
1159
+ --foisit-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
1160
+ --foisit-text: #333;
1161
+ --foisit-backdrop: blur(20px);
983
1162
 
984
1163
  /* Input */
985
- --foisit-input-color: white;
986
- --foisit-input-placeholder: rgba(235, 235, 245, 0.5);
1164
+ --foisit-input-color: #333;
1165
+ --foisit-input-placeholder: rgba(60, 60, 67, 0.6);
987
1166
 
988
1167
  /* Bubbles */
989
- --foisit-bubble-user-bg: rgba(255, 255, 255, 0.1);
990
- --foisit-bubble-user-text: white;
1168
+ --foisit-bubble-user-bg: rgba(0, 0, 0, 0.04);
1169
+ --foisit-bubble-user-text: #333;
991
1170
 
992
- --foisit-bubble-sys-bg: rgba(255, 255, 255, 0.05);
993
- --foisit-bubble-sys-text: rgba(255, 255, 255, 0.9);
1171
+ --foisit-bubble-sys-bg: rgba(255, 255, 255, 0.45);
1172
+ --foisit-bubble-sys-text: #333;
994
1173
 
995
1174
  /* Form Colors */
996
- --foisit-req-star: #f87171;
997
- --foisit-error-text: #fca5a5;
998
- --foisit-error-border: #f87171;
1175
+ --foisit-req-star: #ef4444; /* Red asterisk */
1176
+ --foisit-error-text: #dc2626;
1177
+ --foisit-error-border: #fca5a5;
999
1178
  }
1000
- }
1179
+
1180
+ @media (prefers-color-scheme: dark) {
1181
+ :root {
1182
+ /* DARK MODE */
1183
+ --foisit-bg: linear-gradient(135deg, rgba(40, 40, 40, 0.65), rgba(40, 40, 40, 0.25));
1184
+ --foisit-border: 1px solid rgba(255, 255, 255, 0.1);
1185
+ --foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
1186
+ --foisit-text: #fff;
1187
+ --foisit-backdrop: blur(20px);
1188
+
1189
+ /* Input */
1190
+ --foisit-input-color: white;
1191
+ --foisit-input-placeholder: rgba(235, 235, 245, 0.5);
1192
+
1193
+ /* Bubbles */
1194
+ --foisit-bubble-user-bg: rgba(255, 255, 255, 0.1);
1195
+ --foisit-bubble-user-text: white;
1196
+
1197
+ --foisit-bubble-sys-bg: rgba(255, 255, 255, 0.05);
1198
+ --foisit-bubble-sys-text: rgba(255, 255, 255, 0.9);
1199
+
1200
+ /* Form Colors */
1201
+ --foisit-req-star: #f87171;
1202
+ --foisit-error-text: #fca5a5;
1203
+ --foisit-error-border: #f87171;
1204
+ }
1205
+ }
1206
+ `;
1207
+ t.textContent += `
1001
1208
 
1002
1209
  @keyframes foisitPulse {
1003
1210
  0%, 100% { transform: scale(0.8); opacity: 0.5; }
@@ -1045,8 +1252,8 @@ class Y {
1045
1252
  border: var(--foisit-border);
1046
1253
  box-shadow: var(--foisit-shadow);
1047
1254
 
1048
- backdrop-filter: blur(20px);
1049
- -webkit-backdrop-filter: blur(20px);
1255
+ backdrop-filter: var(--foisit-backdrop);
1256
+ -webkit-backdrop-filter: var(--foisit-backdrop);
1050
1257
 
1051
1258
  border-radius: 18px;
1052
1259
  display: none;
@@ -1255,8 +1462,8 @@ class Y {
1255
1462
  border: 1px solid rgba(255,255,255,0.2);
1256
1463
  background: var(--foisit-bg);
1257
1464
  color: var(--foisit-text);
1258
- backdrop-filter: blur(10px);
1259
- -webkit-backdrop-filter: blur(10px);
1465
+ backdrop-filter: var(--foisit-backdrop);
1466
+ -webkit-backdrop-filter: var(--foisit-backdrop);
1260
1467
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
1261
1468
  cursor: pointer;
1262
1469
  pointer-events: auto;
@@ -1268,18 +1475,108 @@ class Y {
1268
1475
  transition: transform 0.2s;
1269
1476
  }
1270
1477
  .foisit-floating-btn:hover { transform: scale(1.05); }
1478
+
1479
+ /* Markdown Styles */
1480
+ .foisit-bubble.system .foisit-md-p { margin: 0 0 0.5em 0; }
1481
+ .foisit-bubble.system .foisit-md-p:last-child { margin-bottom: 0; }
1482
+
1483
+ .foisit-bubble.system .foisit-md-h1,
1484
+ .foisit-bubble.system .foisit-md-h2,
1485
+ .foisit-bubble.system .foisit-md-h3,
1486
+ .foisit-bubble.system .foisit-md-h4,
1487
+ .foisit-bubble.system .foisit-md-h5,
1488
+ .foisit-bubble.system .foisit-md-h6 {
1489
+ margin: 0.8em 0 0.4em 0;
1490
+ font-weight: 600;
1491
+ line-height: 1.3;
1492
+ }
1493
+ .foisit-bubble.system .foisit-md-h1:first-child,
1494
+ .foisit-bubble.system .foisit-md-h2:first-child,
1495
+ .foisit-bubble.system .foisit-md-h3:first-child { margin-top: 0; }
1496
+
1497
+ .foisit-bubble.system .foisit-md-h1 { font-size: 1.4em; }
1498
+ .foisit-bubble.system .foisit-md-h2 { font-size: 1.25em; }
1499
+ .foisit-bubble.system .foisit-md-h3 { font-size: 1.1em; }
1500
+ .foisit-bubble.system .foisit-md-h4 { font-size: 1em; }
1501
+ .foisit-bubble.system .foisit-md-h5 { font-size: 0.95em; }
1502
+ .foisit-bubble.system .foisit-md-h6 { font-size: 0.9em; opacity: 0.85; }
1503
+
1504
+ .foisit-bubble.system .foisit-md-ul,
1505
+ .foisit-bubble.system .foisit-md-ol {
1506
+ margin: 0.5em 0;
1507
+ padding-left: 1.5em;
1508
+ }
1509
+ .foisit-bubble.system .foisit-md-li { margin: 0.25em 0; }
1510
+
1511
+ .foisit-bubble.system .foisit-code-block {
1512
+ background: rgba(0,0,0,0.15);
1513
+ border-radius: 6px;
1514
+ padding: 10px 12px;
1515
+ margin: 0.5em 0;
1516
+ overflow-x: auto;
1517
+ font-family: 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
1518
+ font-size: 0.85em;
1519
+ line-height: 1.4;
1520
+ }
1521
+ .foisit-bubble.system .foisit-code-block code {
1522
+ background: transparent;
1523
+ padding: 0;
1524
+ }
1525
+
1526
+ .foisit-bubble.system .foisit-inline-code {
1527
+ background: rgba(0,0,0,0.1);
1528
+ padding: 2px 6px;
1529
+ border-radius: 4px;
1530
+ font-family: 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
1531
+ font-size: 0.9em;
1532
+ }
1533
+
1534
+ .foisit-bubble.system .foisit-md-blockquote {
1535
+ border-left: 3px solid rgba(127,127,127,0.4);
1536
+ margin: 0.5em 0;
1537
+ padding-left: 12px;
1538
+ opacity: 0.9;
1539
+ font-style: italic;
1540
+ }
1541
+
1542
+ .foisit-bubble.system .foisit-md-link {
1543
+ color: inherit;
1544
+ text-decoration: underline;
1545
+ opacity: 0.9;
1546
+ }
1547
+ .foisit-bubble.system .foisit-md-link:hover { opacity: 1; }
1548
+
1549
+ .foisit-bubble.system .foisit-md-hr {
1550
+ border: none;
1551
+ border-top: 1px solid rgba(127,127,127,0.3);
1552
+ margin: 0.8em 0;
1553
+ }
1554
+
1555
+ .foisit-bubble.system strong { font-weight: 600; }
1556
+ .foisit-bubble.system em { font-style: italic; }
1557
+ .foisit-bubble.system del { text-decoration: line-through; opacity: 0.7; }
1558
+
1559
+ @media (prefers-color-scheme: dark) {
1560
+ .foisit-bubble.system .foisit-code-block { background: rgba(255,255,255,0.08); }
1561
+ .foisit-bubble.system .foisit-inline-code { background: rgba(255,255,255,0.1); }
1562
+ }
1271
1563
  `, document.head.appendChild(t);
1272
1564
  }
1273
1565
  }
1274
- class K {
1566
+ class Y {
1275
1567
  constructor(t) {
1276
- this.config = t, this.isActivated = !1, this.lastProcessedInput = "", this.processingLock = !1, this.defaultIntroMessage = "How can I help you?", this.commandHandler = new B({
1568
+ this.config = t, this.isActivated = !1, this.lastProcessedInput = "", this.processingLock = !1, this.defaultIntroMessage = "How can I help you?", this.commandHandler = new N({
1277
1569
  enableSmartIntent: this.config.enableSmartIntent !== !1,
1278
1570
  intentEndpoint: this.config.intentEndpoint
1279
- }), this.fallbackHandler = new z(), typeof window < "u" && typeof document < "u" ? (this.voiceProcessor = new O(), this.textToSpeech = new H(), this.stateManager = new G(), this.gestureHandler = new U(), this.overlayManager = new Y({
1571
+ }), this.fallbackHandler = new z(), typeof window < "u" && typeof document < "u" ? (this.voiceProcessor = new W(), this.textToSpeech = new P(), this.stateManager = new _(), this.gestureHandler = new R(), this.overlayManager = new G({
1280
1572
  floatingButton: this.config.floatingButton,
1281
- inputPlaceholder: this.config.inputPlaceholder
1282
- }), 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(
1573
+ inputPlaceholder: this.config.inputPlaceholder,
1574
+ enableGestureActivation: this.config.enableGestureActivation,
1575
+ theme: this.config.theme,
1576
+ themeColors: this.config.themeColors
1577
+ }), this.overlayManager.setExternalCommandExecutor(async (e) => this.commandHandler.executeCommand(e)), this.config.commands.forEach(
1578
+ (e) => this.commandHandler.addCommand(e)
1579
+ ), this.config.fallbackResponse && this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse), this.overlayManager.registerCallbacks(
1283
1580
  async (e) => {
1284
1581
  if (typeof e == "string") {
1285
1582
  this.overlayManager.addMessage(e, "user"), await this.handleCommand(e);
@@ -1288,24 +1585,32 @@ class K {
1288
1585
  if (e && typeof e == "object") {
1289
1586
  const i = e, s = i.label ?? i.commandId ?? "Selection";
1290
1587
  this.overlayManager.addMessage(String(s), "user"), this.overlayManager.showLoading();
1291
- const n = await this.commandHandler.executeCommand(i);
1588
+ const n = await this.commandHandler.executeCommand(
1589
+ i
1590
+ );
1292
1591
  this.overlayManager.hideLoading(), this.processResponse(n);
1293
1592
  }
1294
1593
  },
1295
1594
  () => console.log("AssistantService: Overlay closed.")
1296
- )) : (this.voiceProcessor = void 0, this.textToSpeech = void 0, this.stateManager = void 0, this.gestureHandler = void 0, this.overlayManager = void 0, this.config.commands.forEach((e) => this.commandHandler.addCommand(e)), this.config.fallbackResponse && this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse));
1595
+ )) : (this.voiceProcessor = void 0, this.textToSpeech = void 0, this.stateManager = void 0, this.gestureHandler = void 0, this.overlayManager = void 0, this.config.commands.forEach(
1596
+ (e) => this.commandHandler.addCommand(e)
1597
+ ), this.config.fallbackResponse && this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse));
1297
1598
  }
1298
1599
  /** Start listening for activation and commands */
1299
1600
  startListening() {
1300
1601
  if (typeof window > "u" || !this.voiceProcessor) {
1301
- console.log("AssistantService: Voice is disabled or unavailable; startListening() is a no-op.");
1602
+ console.log(
1603
+ "AssistantService: Voice is disabled or unavailable; startListening() is a no-op."
1604
+ );
1302
1605
  return;
1303
1606
  }
1304
1607
  }
1305
1608
  /** Stop listening */
1306
1609
  stopListening() {
1307
1610
  if (typeof window > "u" || !this.voiceProcessor) {
1308
- console.log("AssistantService: Voice unavailable; stopListening() is a no-op."), this.isActivated = !1;
1611
+ console.log(
1612
+ "AssistantService: Voice unavailable; stopListening() is a no-op."
1613
+ ), this.isActivated = !1;
1309
1614
  return;
1310
1615
  }
1311
1616
  this.voiceProcessor.stopListening(), this.isActivated = !1;
@@ -1356,7 +1661,10 @@ class K {
1356
1661
  return;
1357
1662
  }
1358
1663
  if (e.type === "error") {
1359
- this.fallbackHandler.handleFallback(t), this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), "system");
1664
+ this.fallbackHandler.handleFallback(t), this.overlayManager.addMessage(
1665
+ this.fallbackHandler.getFallbackMessage(),
1666
+ "system"
1667
+ );
1360
1668
  return;
1361
1669
  }
1362
1670
  if ((e.type === "ambiguous" || e.type === "confirm") && e.options) {
@@ -1382,7 +1690,9 @@ class K {
1382
1690
  this.overlayManager.showLoading();
1383
1691
  let i;
1384
1692
  try {
1385
- i = await this.commandHandler.executeCommand(e);
1693
+ i = await this.commandHandler.executeCommand(
1694
+ e
1695
+ );
1386
1696
  } finally {
1387
1697
  this.overlayManager.hideLoading();
1388
1698
  }
@@ -1406,6 +1716,19 @@ class K {
1406
1716
  removeCommand(t) {
1407
1717
  console.log(`AssistantService: Removing command "${t}".`), this.commandHandler.removeCommand(t);
1408
1718
  }
1719
+ /** Expose programmatic command handler registration to host apps */
1720
+ registerCommandHandler(t, e) {
1721
+ this.overlayManager && this.overlayManager.registerCommandHandler(t, e);
1722
+ }
1723
+ unregisterCommandHandler(t) {
1724
+ this.overlayManager && this.overlayManager.unregisterCommandHandler(t);
1725
+ }
1726
+ /** Programmatically run a registered command (proxies to OverlayManager) */
1727
+ async runCommand(t) {
1728
+ if (!this.overlayManager) throw new Error("Overlay manager not available.");
1729
+ const e = await this.overlayManager.runCommand(t);
1730
+ return e && typeof e == "object" && "type" in e && this.processResponse(e), e;
1731
+ }
1409
1732
  /** Get all registered commands */
1410
1733
  getCommands() {
1411
1734
  return this.commandHandler.getCommands();
@@ -1421,13 +1744,15 @@ class K {
1421
1744
  if (i && typeof i == "object") {
1422
1745
  const s = i, n = s.label ?? s.commandId ?? "Selection";
1423
1746
  this.overlayManager.addMessage(String(n), "user"), this.overlayManager.showLoading();
1424
- let o;
1747
+ let a;
1425
1748
  try {
1426
- o = await this.commandHandler.executeCommand(s);
1749
+ a = await this.commandHandler.executeCommand(
1750
+ s
1751
+ );
1427
1752
  } finally {
1428
1753
  this.overlayManager.hideLoading();
1429
1754
  }
1430
- this.processResponse(o);
1755
+ this.processResponse(a);
1431
1756
  }
1432
1757
  },
1433
1758
  () => {
@@ -1437,21 +1762,21 @@ class K {
1437
1762
  }
1438
1763
  }
1439
1764
  const q = F(null);
1440
- let I = null;
1765
+ let M = null;
1441
1766
  const Z = ({ config: d, children: t }) => {
1442
- const [e, i] = R(null), [s, n] = R(!1);
1443
- return P(() => {
1444
- I ? console.warn(
1767
+ const [e, i] = I(null), [s, n] = I(!1);
1768
+ return H(() => {
1769
+ M ? console.warn(
1445
1770
  "Multiple AssistantProvider instances detected. Reusing global AssistantService."
1446
- ) : (console.log("Initializing global AssistantService..."), I = new K(d));
1447
- const o = I;
1448
- return i(o), n(!0), () => {
1449
- var r;
1450
- console.log("Cleaning up AssistantService..."), (r = o.destroy) == null || r.call(o), I = null;
1771
+ ) : (console.log("Initializing global AssistantService..."), M = new Y(d));
1772
+ const a = M;
1773
+ return i(a), n(!0), () => {
1774
+ var o;
1775
+ console.log("Cleaning up AssistantService..."), (o = a.destroy) == null || o.call(a), M = null;
1451
1776
  };
1452
1777
  }, [d]), !s || !e ? /* @__PURE__ */ k("div", { children: "Loading Assistant..." }) : /* @__PURE__ */ k(q.Provider, { value: e, children: t });
1453
- }, X = () => {
1454
- const d = N(q);
1778
+ }, K = () => {
1779
+ const d = B(q);
1455
1780
  if (console.log("assistant", d), !d)
1456
1781
  throw new Error("useAssistant must be used within an AssistantProvider");
1457
1782
  return d;
@@ -1459,13 +1784,13 @@ const Z = ({ config: d, children: t }) => {
1459
1784
  label: d = "Activate Assistant",
1460
1785
  onActivate: t
1461
1786
  }) => {
1462
- const e = X();
1787
+ const e = K();
1463
1788
  return /* @__PURE__ */ k("button", { onClick: () => {
1464
1789
  t && t(), e.reactivate();
1465
1790
  }, className: "assistant-activator", children: d });
1466
1791
  }, et = (d) => {
1467
- const [t, e] = R(d.getState());
1468
- return P(() => {
1792
+ const [t, e] = I(d.getState());
1793
+ return H(() => {
1469
1794
  const i = (s) => {
1470
1795
  e(s);
1471
1796
  };
@@ -1479,8 +1804,8 @@ export {
1479
1804
  tt as AssistantActivator,
1480
1805
  q as AssistantContext,
1481
1806
  Z as AssistantProvider,
1482
- K as AssistantService,
1483
- J as ReactWrapper,
1484
- X as useAssistant,
1807
+ Y as AssistantService,
1808
+ Q as ReactWrapper,
1809
+ K as useAssistant,
1485
1810
  et as useAssistantState
1486
1811
  };