@foisit/react-wrapper 2.4.1 → 2.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +22 -3
  2. package/index.js +3 -3
  3. package/index.mjs +243 -178
  4. package/package.json +15 -2
package/README.md CHANGED
@@ -134,6 +134,28 @@ Define parameters and Foisit will automatically generate forms to collect them:
134
134
  }
135
135
  ```
136
136
 
137
+ **Enterprise-safe param collection controls**
138
+
139
+ - `collectRequiredViaForm` (default: true): force missing/invalid required params to be collected via a form instead of conversational guessing.
140
+ - `allowAiParamExtraction` (default: true): when false, ignore AI-extracted params and always prompt the user for required fields.
141
+
142
+ Example:
143
+
144
+ ```tsx
145
+ {
146
+ command: 'secure create user',
147
+ description: 'No AI guessing, form-only',
148
+ collectRequiredViaForm: true,
149
+ allowAiParamExtraction: false,
150
+ parameters: [
151
+ { name: 'fullName', type: 'string', required: true },
152
+ { name: 'email', type: 'string', required: true },
153
+ { name: 'age', type: 'number', required: true, min: 18 },
154
+ ],
155
+ action: (params) => userService.create(params),
156
+ }
157
+ ```
158
+
137
159
  **Supported Parameter Types:**
138
160
 
139
161
  - `string` - Text input
@@ -532,8 +554,6 @@ function AccountManager() {
532
554
  ### Full Type Definitions
533
555
 
534
556
  ```tsx
535
- import { AssistantCommand, InteractiveResponse } from '@foisit/core';
536
-
537
557
  // Type-safe command definition
538
558
  const myCommand: AssistantCommand = {
539
559
  command: 'update settings',
@@ -646,7 +666,6 @@ test('renders assistant', () => {
646
666
 
647
667
  ## Related Packages
648
668
 
649
- - **[@foisit/core](../core)** - Core engine (auto-installed)
650
669
  - **[@foisit/angular-wrapper](../angular-wrapper)** - Angular integration
651
670
  - **[@foisit/vue-wrapper](../vue-wrapper)** - Vue integration
652
671
 
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("react/jsx-runtime"),S=require("react"),W={};function N(){return A.jsx("div",{className:W.container,children:A.jsx("h1",{children:"Welcome to ReactWrapper!"})})}class T{constructor(t){this.endpoint=t||"https://foisit-ninja.netlify.app/.netlify/functions/intent"}async determineIntent(t,e,i){try{const s={userInput:t,commands:e.map(r=>({id:r.id,command:r.command,description:r.description,parameters:r.parameters})),context:i},n=await fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!n.ok)throw new Error(`Proxy API Error: ${n.statusText}`);return await n.json()}catch(s){return console.error("OpenAIService Error:",s),{type:"unknown"}}}}class B{constructor(t=!0){if(this.commands=new Map,this.openAIService=null,this.context=null,this.pendingConfirmation=null,this.enableSmartIntent=!0,this.selectOptionsCache=new Map,typeof t=="boolean"){this.enableSmartIntent=t,this.enableSmartIntent&&(this.openAIService=new T);return}this.enableSmartIntent=t.enableSmartIntent??!0,this.enableSmartIntent&&(this.openAIService=new T(t.intentEndpoint))}addCommand(t,e){let i;if(typeof t=="string"){if(!e)throw new Error("Action required when adding command by string.");i={id:t.toLowerCase().replace(/\s+/g,"_"),command:t.toLowerCase(),action:e}}else i={...t},i.id||(i.id=i.command.toLowerCase().replace(/\s+/g,"_"));this.commands.set(i.command.toLowerCase(),i),i.id&&(i.id,i.command)}removeCommand(t){this.commands.delete(t.toLowerCase())}async executeCommand(t){if(typeof t=="object"&&t!==null){if(this.isStructured(t)){const c=String(t.commandId),f=t.params??{},o=this.getCommandById(c);if(!o)return{message:"That command is not available.",type:"error"};const p=this.sanitizeParamsForCommand(o,f),h=(o.parameters??[]).filter(C=>C.required).filter(C=>p[C.name]==null||p[C.name]==="");return h.length>0?(this.context={commandId:this.getCommandIdentifier(o),params:p},{message:`Please provide the required details for "${o.command}".`,type:"form",fields:h}):o.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(o),params:p},this.buildConfirmResponse(o)):this.safeRunAction(o,p)}if(!this.context)return{message:"Session expired or invalid context.",type:"error"};const n=this.getCommandById(this.context.commandId);if(!n)return this.context=null,{message:"Session expired or invalid context.",type:"error"};if(Array.isArray(t))return{message:"Invalid form payload.",type:"error"};const a={...this.context.params,...t};if(n.critical)return this.context=null,this.pendingConfirmation={commandId:this.getCommandIdentifier(n),params:a},this.buildConfirmResponse(n);const r=await this.safeRunAction(n,a);return this.context=null,this.normalizeResponse(r)}const e=t.trim().toLowerCase();if(this.pendingConfirmation){const n=e;if(["yes","y","confirm","ok","okay"].includes(n)){const{commandId:a,params:r}=this.pendingConfirmation;this.pendingConfirmation=null;const c=this.getCommandById(a);return c?this.safeRunAction(c,r):{message:"That action is no longer available.",type:"error"}}return["no","n","cancel","stop"].includes(n)?(this.pendingConfirmation=null,{message:"✅ Cancelled.",type:"success"}):{message:"Please confirm: Yes or No.",type:"confirm",options:[{label:"Yes",value:"yes"},{label:"No",value:"no"}]}}const i=this.commands.get(e);if(i){const n=i,a=(n.parameters??[]).filter(r=>r.required);return a.length>0?(this.context={commandId:this.getCommandIdentifier(n),params:{}},{message:`Please provide the required details for "${n.command}".`,type:"form",fields:a}):n.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(n),params:{}},this.buildConfirmResponse(n)):this.safeRunAction(n,{})}const s=await this.tryDeterministicMatch(e);if(s)return s;if(this.enableSmartIntent&&this.openAIService){const n=await this.getCommandsForAI(),a=await this.openAIService.determineIntent(e,n,this.context);return this.handleAIResult(a)}return this.enableSmartIntent?this.listAllCommands():{message:"I'm not sure what you mean.",type:"error"}}async handleAIResult(t){if(t.type==="match"&&t.match){const e=this.getCommandById(t.match);if(!e)return{message:"I'm not sure what you mean.",type:"error"};const i=t.params??{},s=this.sanitizeParamsForCommand(e,i),a=(e.parameters??[]).filter(c=>c.required).filter(c=>s[c.name]==null||s[c.name]==="");if(t.incomplete||a.length>0){this.context={commandId:this.getCommandIdentifier(e),params:s};const c=a.some(f=>f.type==="select");if(a.length<=2&&!c){const f=a.map(o=>o.name).join(" and ");return{message:t.message||`Please provide ${f}.`,type:"question"}}return{message:t.message||`Please fill in the missing details for "${e.command}".`,type:"form",fields:a}}if(e.critical)return this.pendingConfirmation={commandId:this.getCommandIdentifier(e),params:s},this.buildConfirmResponse(e);const r=await e.action(s);return this.normalizeResponse(r)}return t.type==="ambiguous"&&t.options&&t.options.length?{message:t.message||"Did you mean one of these?",type:"ambiguous",options:t.options.map(e=>({label:e.label,value:e.commandId??e.label,commandId:e.commandId}))}:this.listAllCommands()}sanitizeParamsForCommand(t,e){const i={...e??{}};for(const s of t.parameters??[])if(s.type==="date"){const n=i[s.name];if(typeof n=="string"){const a=n.trim();this.isIsoDateString(a)||delete i[s.name]}}return i}isIsoDateString(t){if(!/^\d{4}-\d{2}-\d{2}$/.test(t))return!1;const e=new Date(`${t}T00:00:00Z`);return!Number.isNaN(e.getTime())}buildConfirmResponse(t){return{message:`⚠️ Are you sure you want to run "${t.command}"?`,type:"confirm",options:[{label:"Yes",value:"yes"},{label:"No",value:"no"}]}}async tryDeterministicMatch(t){const e=[];for(const r of this.commands.values()){let c=0;const f=r.command.toLowerCase();t.includes(f)&&(c+=5);const o=r.keywords??[];for(const p of o){const b=p.toLowerCase().trim();b&&(t===b?c+=4:t.includes(b)&&(c+=3))}c>0&&e.push({cmd:r,score:c})}if(e.length===0)return null;e.sort((r,c)=>c.score-r.score);const i=e[0].score,s=e.filter(r=>r.score===i).slice(0,3);if(s.length>1)return{message:"I think you mean one of these. Which one should I run?",type:"ambiguous",options:s.map(r=>({label:r.cmd.command,value:r.cmd.command,commandId:r.cmd.id}))};const n=s[0].cmd,a=(n.parameters??[]).filter(r=>r.required);return a.length>0?(this.context={commandId:this.getCommandIdentifier(n),params:{}},{message:`Please provide the required details for "${n.command}".`,type:"form",fields:a}):n.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(n),params:{}},this.buildConfirmResponse(n)):this.safeRunAction(n,{})}async safeRunAction(t,e){try{const i=await t.action(e??{});return this.normalizeResponse(i)}catch{return{message:"Something went wrong while running that command.",type:"error"}}}async getCommandsForAI(){const t=Array.from(this.commands.values()).map(e=>({...e,parameters:e.parameters?e.parameters.map(i=>({...i})):void 0}));return await Promise.all(t.map(async e=>{e.parameters&&await Promise.all(e.parameters.map(async i=>{if(i.type!=="select"||!i.getOptions||i.options&&i.options.length)return;const s=`${e.id??e.command}:${i.name}`,n=this.selectOptionsCache.get(s),a=Date.now();if(n&&a-n.ts<6e4){i.options=n.options;return}try{const r=await i.getOptions();this.selectOptionsCache.set(s,{options:r,ts:a}),i.options=r}catch{}}))})),t}getCommandById(t){for(const e of this.commands.values())if(e.id===t)return e}listAllCommands(){return{message:"Here are the available commands:",type:"ambiguous",options:Array.from(this.commands.values()).map(e=>({label:e.command,value:e.id??e.command,commandId:e.id??e.command}))}}normalizeResponse(t){return typeof t=="string"?{message:t,type:"success"}:t&&typeof t=="object"?t:{message:"Done",type:"success"}}isStructured(t){return typeof t.commandId=="string"}getCommandIdentifier(t){return t.id||(t.id=t.command.toLowerCase().replace(/\s+/g,"_")),t.id}getCommands(){return Array.from(this.commands.keys())}}class q{constructor(){this.synth=window.speechSynthesis}speak(t,e){if(!this.synth){console.error("SpeechSynthesis API is not supported in this browser.");return}const i=new SpeechSynthesisUtterance(t);e&&(i.pitch=e.pitch||1,i.rate=e.rate||1,i.volume=e.volume||1),i.onstart=()=>{window.dispatchEvent(new CustomEvent("foisit:tts-start"))},i.onend=()=>{console.log("Speech finished."),window.dispatchEvent(new CustomEvent("foisit:tts-end"))},i.onerror=s=>{console.error("Error during speech synthesis:",s.error)},this.synth.speak(i)}stopSpeaking(){this.synth&&this.synth.cancel()}}class O{constructor(){this.fallbackMessage="Sorry, I didn’t understand that."}setFallbackMessage(t){this.fallbackMessage=t}handleFallback(t){t&&console.log(`Fallback triggered for: "${t}"`),console.log(this.fallbackMessage),new q().speak(this.fallbackMessage)}getFallbackMessage(){return this.fallbackMessage}}const P=()=>{const d=window;return d.SpeechRecognition??d.webkitSpeechRecognition??null};class z{constructor(t="en-US",e={}){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=()=>{var s;this.ttsSpeaking=!0;try{(s=this.recognition)==null||s.stop()}catch{}this.isListening&&this.emitStatus("speaking")},this.onTTSEnd=()=>{this.ttsSpeaking=!1,this.isListening&&this.restartAllowed?this.safeRestart():this.emitStatus(this.isListening?"listening":"idle")};const i=P();if(i){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=()=>{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")};const s=this.recognition;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)}else this.recognition=null,this.emitStatus("unsupported");window.addEventListener("foisit:tts-start",this.onTTSStart),window.addEventListener("foisit:tts-end",this.onTTSEnd),this.visibilityHandler=()=>{var s;if(document.hidden){try{(s=this.recognition)==null||s.stop()}catch{}this.emitStatus(this.ttsSpeaking?"speaking":"idle")}else this.isListening&&!this.ttsSpeaking&&this.safeRestart()},document.addEventListener("visibilitychange",this.visibilityHandler)}log(t){this.debugEnabled&&t&&console.log("[VoiceProcessor]",t)}warn(t){this.debugEnabled&&t&&console.warn("[VoiceProcessor]",t)}error(t){this.debugEnabled&&t&&console.error("[VoiceProcessor]",t)}isSupported(){return P()!==null}onStatusChange(t){this.statusCallback=t}startListening(t){if(!this.isSupported()||!this.recognition){this.warn("VoiceProcessor: SpeechRecognition is not supported in this browser."),this.emitStatus("unsupported");return}if(this.isListening){this.warn("VoiceProcessor: Already listening."),this.resultCallback=t;return}this.resultCallback=t,this.intentionallyStopped=!1,this.restartAllowed=!0,this.isListening=!0,this.emitStatus("listening"),this.prewarmAudio().finally(()=>{this.safeRestart()})}stopListening(){var t;this.intentionallyStopped=!0,this.restartAllowed=!1,this.isListening=!1,this.emitStatus(this.ttsSpeaking?"speaking":"idle");try{(t=this.recognition)==null||t.stop()}catch{}}destroy(){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)}handleResult(t,e){var s,n;if(!this.resultCallback)return;const i=e.confidenceThreshold??.6;for(let a=t.resultIndex;a<t.results.length;a++){const r=t.results[a],c=r&&r[0],f=((n=(s=c==null?void 0:c.transcript)==null?void 0:s.trim)==null?void 0:n.call(s))||"",o=(c==null?void 0:c.confidence)??0;if(f&&!(!r.isFinal&&e.interimResults===!1)&&!(r.isFinal&&o<i))try{this.hadResultThisSession=!0,this.resultCallback(f,!!r.isFinal)}catch{this.error("VoiceProcessor: result callback error")}}}handleEnd(){if(this.log("recognition onend"),this.engineActive=!1,this.destroyed||this.intentionallyStopped||!this.restartAllowed||this.ttsSpeaking){this.ttsSpeaking||(this.isListening=!1,this.emitStatus("idle"));return}this.isListening=!0,this.scheduleRestart()}handleError(t){const e=t==null?void 0:t.error;if(this.warn(`Error occurred: ${e??"unknown"}`),e&&["not-allowed","service-not-allowed","bad-grammar","language-not-supported"].includes(e)){this.intentionallyStopped=!0,this.restartAllowed=!1,this.isListening=!1,this.emitStatus("error",{error:e});return}this.scheduleRestart()}safeRestart(){if(!this.recognition)return;if(this.engineActive){this.log("safeRestart: engine already active, skipping start");return}const t=Date.now();if(t-this.lastStart<300){setTimeout(()=>this.safeRestart(),300);return}this.lastStart=t;try{this.log("calling recognition.start()"),this.recognition.start(),this.backoffMs=250,this.isListening&&!this.ttsSpeaking&&this.emitStatus("listening")}catch{this.error("recognition.start() threw; scheduling restart"),this.scheduleRestart()}}scheduleRestart(){if(this.destroyed||this.intentionallyStopped||!this.restartAllowed||this.ttsSpeaking)return;if(this.engineActive){this.log("scheduleRestart: engine active, not scheduling");return}const t=Math.min(this.backoffMs,2e3);if(this.log(`scheduleRestart in ${t}ms`),this.restartTimer){this.log("scheduleRestart: restart already scheduled");return}this.restartTimer=setTimeout(()=>{this.restartTimer=null,!(this.destroyed||this.intentionallyStopped||!this.restartAllowed||this.ttsSpeaking)&&this.safeRestart()},t),this.backoffMs=Math.min(this.backoffMs*2,2e3)}async prewarmAudio(){if(!this.prewarmed)try{if(typeof navigator>"u"||!("mediaDevices"in navigator))return;const t=navigator.mediaDevices;if(!(t!=null&&t.getUserMedia))return;this.log("prewarmAudio: requesting mic");const e=await t.getUserMedia({audio:!0});for(const i of e.getTracks())i.stop();this.prewarmed=!0,this.log("prewarmAudio: mic ready")}catch{this.warn("prewarmAudio: failed to get mic")}}emitStatus(t,e){if(this.statusCallback)try{this.statusCallback(t,e)}catch{this.error("VoiceProcessor: status callback error")}}}class j{constructor(){this.lastTap=0}setupDoubleTapListener(t){this.destroy(),this.dblClickListener=()=>{t()},document.addEventListener("dblclick",this.dblClickListener),this.touchEndListener=()=>{const e=new Date().getTime(),i=e-this.lastTap;i<300&&i>0&&t(),this.lastTap=e},document.addEventListener("touchend",this.touchEndListener)}destroy(){this.dblClickListener&&document.removeEventListener("dblclick",this.dblClickListener),this.touchEndListener&&document.removeEventListener("touchend",this.touchEndListener),this.dblClickListener=void 0,this.touchEndListener=void 0}}function $(){if(document.querySelector("#assistant-styles")){console.log("Styles already injected");return}const t=document.createElement("style");t.id="assistant-styles",t.innerHTML=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("react/jsx-runtime"),C=require("react"),N={};function W(){return A.jsx("div",{className:N.container,children:A.jsx("h1",{children:"Welcome to ReactWrapper!"})})}class T{constructor(t){this.endpoint=t||"https://foisit-ninja.netlify.app/.netlify/functions/intent"}async determineIntent(t,e,i){try{const s={userInput:t,commands:e.map(r=>({id:r.id,command:r.command,description:r.description,parameters:r.parameters})),context:i},n=await fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!n.ok)throw new Error(`Proxy API Error: ${n.statusText}`);return await n.json()}catch(s){return console.error("OpenAIService Error:",s),{type:"unknown"}}}}class B{constructor(t=!0){if(this.commands=new Map,this.openAIService=null,this.context=null,this.pendingConfirmation=null,this.enableSmartIntent=!0,this.selectOptionsCache=new Map,typeof t=="boolean"){this.enableSmartIntent=t,this.enableSmartIntent&&(this.openAIService=new T);return}this.enableSmartIntent=t.enableSmartIntent??!0,this.enableSmartIntent&&(this.openAIService=new T(t.intentEndpoint))}addCommand(t,e){let i;if(typeof t=="string"){if(!e)throw new Error("Action required when adding command by string.");i={id:t.toLowerCase().replace(/\s+/g,"_"),command:t.toLowerCase(),action:e}}else i={...t},i.id||(i.id=i.command.toLowerCase().replace(/\s+/g,"_"));this.commands.set(i.command.toLowerCase(),i),i.id&&(i.id,i.command)}removeCommand(t){this.commands.delete(t.toLowerCase())}async executeCommand(t){if(typeof t=="object"&&t!==null){if(this.isStructured(t)){const c=String(t.commandId),f=t.params??{},a=this.getCommandById(c);if(!a)return{message:"That command is not available.",type:"error"};const p=this.sanitizeParamsForCommand(a,f),m=(a.parameters??[]).filter(S=>S.required).filter(S=>p[S.name]==null||p[S.name]==="");return m.length>0?(this.context={commandId:this.getCommandIdentifier(a),params:p},{message:`Please provide the required details for "${a.command}".`,type:"form",fields:m}):a.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(a),params:p},this.buildConfirmResponse(a)):this.safeRunAction(a,p)}if(!this.context)return{message:"Session expired or invalid context.",type:"error"};const n=this.getCommandById(this.context.commandId);if(!n)return this.context=null,{message:"Session expired or invalid context.",type:"error"};if(Array.isArray(t))return{message:"Invalid form payload.",type:"error"};const o={...this.context.params,...t};if(n.critical)return this.context=null,this.pendingConfirmation={commandId:this.getCommandIdentifier(n),params:o},this.buildConfirmResponse(n);const r=await this.safeRunAction(n,o);return this.context=null,this.normalizeResponse(r)}const e=t.trim().toLowerCase();if(this.pendingConfirmation){const n=e;if(["yes","y","confirm","ok","okay"].includes(n)){const{commandId:o,params:r}=this.pendingConfirmation;this.pendingConfirmation=null;const c=this.getCommandById(o);return c?this.safeRunAction(c,r):{message:"That action is no longer available.",type:"error"}}return["no","n","cancel","stop"].includes(n)?(this.pendingConfirmation=null,{message:"Cancelled.",type:"success"}):{message:"Please confirm: Yes or No.",type:"confirm",options:[{label:"Yes",value:"yes"},{label:"No",value:"no"}]}}const i=this.commands.get(e);if(i){const n=i,o=(n.parameters??[]).filter(r=>r.required);return o.length>0?(this.context={commandId:this.getCommandIdentifier(n),params:{}},{message:`Please provide the required details for "${n.command}".`,type:"form",fields:o}):n.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(n),params:{}},this.buildConfirmResponse(n)):this.safeRunAction(n,{})}const s=await this.tryDeterministicMatch(e);if(s)return s;if(this.enableSmartIntent&&this.openAIService){const n=await this.getCommandsForAI(),o=await this.openAIService.determineIntent(e,n,this.context);return this.handleAIResult(o)}return this.enableSmartIntent?this.listAllCommands():{message:"I'm not sure what you mean.",type:"error"}}async handleAIResult(t){if(t.type==="match"&&t.match){const e=this.getCommandById(t.match);if(!e)return{message:"I'm not sure what you mean.",type:"error"};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]==="");if(t.incomplete||r.length>0){if(this.context={commandId:this.getCommandIdentifier(e),params:n},!(e.collectRequiredViaForm!==!1)&&this.shouldAskSingleQuestion(r)){const p=r.map(g=>g.name).join(" and ");return{message:t.message||`Please provide ${p}.`,type:"question"}}return{message:t.message||`Please fill in the missing details for "${e.command}".`,type:"form",fields:r}}if(e.critical)return this.pendingConfirmation={commandId:this.getCommandIdentifier(e),params:n},this.buildConfirmResponse(e);const c=await e.action(n);return this.normalizeResponse(c)}return t.type==="ambiguous"&&t.options&&t.options.length?{message:t.message||"Did you mean one of these?",type:"ambiguous",options:t.options.map(e=>({label:e.label,value:e.commandId??e.label,commandId:e.commandId}))}:this.listAllCommands()}sanitizeParamsForCommand(t,e){const i={...e??{}};for(const s of t.parameters??[]){const n=i[s.name];if(s.type==="string"){if(typeof n!="string"){delete i[s.name];continue}const o=n.trim();if(!o){delete i[s.name];continue}i[s.name]=o}if(s.type==="number"){const o=typeof n=="number"?n:Number(n==null?void 0:n.toString().trim());if(Number.isNaN(o)){delete i[s.name];continue}if(typeof s.min=="number"&&o<s.min){delete i[s.name];continue}if(typeof s.max=="number"&&o>s.max){delete i[s.name];continue}i[s.name]=o}if(s.type==="date"){const o=i[s.name];if(typeof o=="string"){const r=o.trim();this.isIsoDateString(r)?i[s.name]=r:delete i[s.name]}else delete i[s.name]}if(s.type==="select"){const o=typeof n=="string"?n:n==null?void 0:n.toString();if(!o){delete i[s.name];continue}if(Array.isArray(s.options)&&s.options.length>0&&!s.options.some(c=>String(c.value)===String(o))){delete i[s.name];continue}i[s.name]=o}if(s.type==="file"){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);(s.delivery??"file")==="base64"?!c&&!r&&delete i[s.name]:r||delete i[s.name]}}return i}isIsoDateString(t){if(!/^\d{4}-\d{2}-\d{2}$/.test(t))return!1;const e=new Date(`${t}T00:00:00Z`);return!Number.isNaN(e.getTime())}shouldAskSingleQuestion(t){if(t.length!==1)return!1;const e=t[0].type;return e==="string"||e==="number"||e==="date"}buildConfirmResponse(t){return{message:`Are you sure you want to run "${t.command}"?`,type:"confirm",options:[{label:"Yes",value:"yes"},{label:"No",value:"no"}]}}async tryDeterministicMatch(t){const e=[];for(const r of this.commands.values()){let c=0;const f=r.command.toLowerCase();t.includes(f)&&(c+=5);const a=r.keywords??[];for(const p of a){const g=p.toLowerCase().trim();g&&(t===g?c+=4:t.includes(g)&&(c+=3))}c>0&&e.push({cmd:r,score:c})}if(e.length===0)return null;e.sort((r,c)=>c.score-r.score);const i=e[0].score,s=e.filter(r=>r.score===i).slice(0,3);if(s.length>1)return{message:"I think you mean one of these. Which one should I run?",type:"ambiguous",options:s.map(r=>({label:r.cmd.command,value:r.cmd.command,commandId:r.cmd.id}))};const n=s[0].cmd,o=(n.parameters??[]).filter(r=>r.required);return o.length>0?(this.context={commandId:this.getCommandIdentifier(n),params:{}},{message:`Please provide the required details for "${n.command}".`,type:"form",fields:o}):n.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(n),params:{}},this.buildConfirmResponse(n)):this.safeRunAction(n,{})}async safeRunAction(t,e){try{const i=await t.action(e??{});return this.normalizeResponse(i)}catch{return{message:"Something went wrong while running that command.",type:"error"}}}async getCommandsForAI(){const t=Array.from(this.commands.values()).map(e=>({...e,parameters:e.parameters?e.parameters.map(i=>({...i})):void 0}));return await Promise.all(t.map(async e=>{e.parameters&&await Promise.all(e.parameters.map(async i=>{if(i.type!=="select"||!i.getOptions||i.options&&i.options.length)return;const s=`${e.id??e.command}:${i.name}`,n=this.selectOptionsCache.get(s),o=Date.now();if(n&&o-n.ts<6e4){i.options=n.options;return}try{const r=await i.getOptions();this.selectOptionsCache.set(s,{options:r,ts:o}),i.options=r}catch{}}))})),t}getCommandById(t){for(const e of this.commands.values())if(e.id===t)return e}listAllCommands(){return{message:"Here are the available commands:",type:"ambiguous",options:Array.from(this.commands.values()).map(e=>({label:e.command,value:e.id??e.command,commandId:e.id??e.command}))}}normalizeResponse(t){return typeof t=="string"?{message:t,type:"success"}:t&&typeof t=="object"?t:{message:"Done",type:"success"}}isStructured(t){return typeof t.commandId=="string"}getCommandIdentifier(t){return t.id||(t.id=t.command.toLowerCase().replace(/\s+/g,"_")),t.id}getCommands(){return Array.from(this.commands.keys())}}class H{constructor(){this.synth=typeof window<"u"?window.speechSynthesis:null}speak(t,e){if(!this.synth){console.error("SpeechSynthesis API is not supported in this environment.");return}const i=new SpeechSynthesisUtterance(t);e&&(i.pitch=e.pitch||1,i.rate=e.rate||1,i.volume=e.volume||1),typeof window<"u"&&(i.onstart=()=>{window.dispatchEvent(new CustomEvent("foisit:tts-start"))},i.onend=()=>{console.log("Speech finished."),window.dispatchEvent(new CustomEvent("foisit:tts-end"))}),i.onerror=s=>{console.error("Error during speech synthesis:",s.error)},this.synth.speak(i)}stopSpeaking(){this.synth&&this.synth.cancel()}}class z{constructor(){this.fallbackMessage="Sorry, I didn’t understand that."}setFallbackMessage(t){this.fallbackMessage=t}handleFallback(t){t&&console.log(`Fallback triggered for: "${t}"`),console.log(this.fallbackMessage),new H().speak(this.fallbackMessage)}getFallbackMessage(){return this.fallbackMessage}}const P=()=>{if(typeof window>"u")return null;const d=window;return d.SpeechRecognition??d.webkitSpeechRecognition??null};class O{constructor(t="en-US",e={}){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=()=>{var s;this.ttsSpeaking=!0;try{(s=this.recognition)==null||s.stop()}catch{}this.isListening&&this.emitStatus("speaking")},this.onTTSEnd=()=>{this.ttsSpeaking=!1,this.isListening&&this.restartAllowed?this.safeRestart():this.emitStatus(this.isListening?"listening":"idle")};const i=P();if(i){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=()=>{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")};const s=this.recognition;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)}else this.recognition=null,this.emitStatus("unsupported");typeof window<"u"?(window.addEventListener("foisit:tts-start",this.onTTSStart),window.addEventListener("foisit:tts-end",this.onTTSEnd),this.visibilityHandler=()=>{var s;if(typeof document<"u"&&document.hidden){try{(s=this.recognition)==null||s.stop()}catch{}this.emitStatus(this.ttsSpeaking?"speaking":"idle")}else this.isListening&&!this.ttsSpeaking&&this.safeRestart()},typeof document<"u"&&document.addEventListener("visibilitychange",this.visibilityHandler)):this.visibilityHandler=void 0}log(t){this.debugEnabled&&t&&console.log("[VoiceProcessor]",t)}warn(t){this.debugEnabled&&t&&console.warn("[VoiceProcessor]",t)}error(t){this.debugEnabled&&t&&console.error("[VoiceProcessor]",t)}isSupported(){return P()!==null}onStatusChange(t){this.statusCallback=t}startListening(t){if(!this.isSupported()||!this.recognition){this.warn("VoiceProcessor: SpeechRecognition is not supported in this browser."),this.emitStatus("unsupported");return}if(this.isListening){this.warn("VoiceProcessor: Already listening."),this.resultCallback=t;return}this.resultCallback=t,this.intentionallyStopped=!1,this.restartAllowed=!0,this.isListening=!0,this.emitStatus("listening"),this.prewarmAudio().finally(()=>{this.safeRestart()})}stopListening(){var t;this.intentionallyStopped=!0,this.restartAllowed=!1,this.isListening=!1,this.emitStatus(this.ttsSpeaking?"speaking":"idle");try{(t=this.recognition)==null||t.stop()}catch{}}destroy(){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)}handleResult(t,e){var s,n;if(!this.resultCallback)return;const i=e.confidenceThreshold??.6;for(let o=t.resultIndex;o<t.results.length;o++){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;if(f&&!(!r.isFinal&&e.interimResults===!1)&&!(r.isFinal&&a<i))try{this.hadResultThisSession=!0,this.resultCallback(f,!!r.isFinal)}catch{this.error("VoiceProcessor: result callback error")}}}handleEnd(){if(this.log("recognition onend"),this.engineActive=!1,this.destroyed||this.intentionallyStopped||!this.restartAllowed||this.ttsSpeaking){this.ttsSpeaking||(this.isListening=!1,this.emitStatus("idle"));return}this.isListening=!0,this.scheduleRestart()}handleError(t){const e=t==null?void 0:t.error;if(this.warn(`Error occurred: ${e??"unknown"}`),e&&["not-allowed","service-not-allowed","bad-grammar","language-not-supported"].includes(e)){this.intentionallyStopped=!0,this.restartAllowed=!1,this.isListening=!1,this.emitStatus("error",{error:e});return}this.scheduleRestart()}safeRestart(){if(!this.recognition)return;if(this.engineActive){this.log("safeRestart: engine already active, skipping start");return}const t=Date.now();if(t-this.lastStart<300){setTimeout(()=>this.safeRestart(),300);return}this.lastStart=t;try{this.log("calling recognition.start()"),this.recognition.start(),this.backoffMs=250,this.isListening&&!this.ttsSpeaking&&this.emitStatus("listening")}catch{this.error("recognition.start() threw; scheduling restart"),this.scheduleRestart()}}scheduleRestart(){if(this.destroyed||this.intentionallyStopped||!this.restartAllowed||this.ttsSpeaking)return;if(this.engineActive){this.log("scheduleRestart: engine active, not scheduling");return}const t=Math.min(this.backoffMs,2e3);if(this.log(`scheduleRestart in ${t}ms`),this.restartTimer){this.log("scheduleRestart: restart already scheduled");return}this.restartTimer=setTimeout(()=>{this.restartTimer=null,!(this.destroyed||this.intentionallyStopped||!this.restartAllowed||this.ttsSpeaking)&&this.safeRestart()},t),this.backoffMs=Math.min(this.backoffMs*2,2e3)}async prewarmAudio(){if(!this.prewarmed)try{if(typeof navigator>"u"||!("mediaDevices"in navigator))return;const t=navigator.mediaDevices;if(!(t!=null&&t.getUserMedia))return;this.log("prewarmAudio: requesting mic");const e=await t.getUserMedia({audio:!0});for(const i of e.getTracks())i.stop();this.prewarmed=!0,this.log("prewarmAudio: mic ready")}catch{this.warn("prewarmAudio: failed to get mic")}}emitStatus(t,e){if(this.statusCallback)try{this.statusCallback(t,e)}catch{this.error("VoiceProcessor: status callback error")}}}class j{constructor(){this.lastTap=0}setupDoubleTapListener(t){this.destroy(),this.dblClickListener=()=>{t()},document.addEventListener("dblclick",this.dblClickListener),this.touchEndListener=()=>{const e=new Date().getTime(),i=e-this.lastTap;i<300&&i>0&&t(),this.lastTap=e},document.addEventListener("touchend",this.touchEndListener)}destroy(){this.dblClickListener&&document.removeEventListener("dblclick",this.dblClickListener),this.touchEndListener&&document.removeEventListener("touchend",this.touchEndListener),this.dblClickListener=void 0,this.touchEndListener=void 0}}function U(){if(document.querySelector("#assistant-styles")){console.log("Styles already injected");return}const t=document.createElement("style");t.id="assistant-styles",t.innerHTML=`
2
2
  /* Rounded shape with gradient animation */
3
3
  .gradient-indicator {
4
4
  position: fixed;
@@ -31,7 +31,7 @@
31
31
  border-radius: 50%;
32
32
  }
33
33
  }
34
- `,document.head.appendChild(t),console.log("Gradient styles injected")}function D(){if(document.querySelector("#gradient-indicator"))return;const d=document.createElement("div");d.id="gradient-indicator",$(),d.classList.add("gradient-indicator"),document.body.appendChild(d),console.log("Gradient indicator added to the DOM")}function U(){const d=document.querySelector("#gradient-indicator");d&&(d.remove(),console.log("Gradient indicator removed from the DOM"))}class V{constructor(){this.state="idle",this.subscribers=[]}getState(){return this.state}setState(t){this.state=t,this.notifySubscribers(),console.log("State updated:",t),t==="listening"?D():U()}subscribe(t){this.subscribers.push(t)}notifySubscribers(){this.subscribers.forEach(t=>t(this.state))}}class G{constructor(t){this.container=null,this.chatWindow=null,this.messagesContainer=null,this.input=null,this.isOpen=!1,this.loadingEl=null,this.config=t,this.init()}init(){var e,i;if(this.container)return;this.injectOverlayStyles();const t=document.getElementById("foisit-overlay-container");if(t&&t instanceof HTMLElement){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();return}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()}renderFloatingButton(){var s,n,a,r,c,f;const t=document.createElement("button");t.innerHTML=((s=this.config.floatingButton)==null?void 0:s.customHtml)||"🎙️";const e=((a=(n=this.config.floatingButton)==null?void 0:n.position)==null?void 0:a.bottom)||"20px",i=((c=(r=this.config.floatingButton)==null?void 0:r.position)==null?void 0:c.right)||"20px";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)}renderChatWindow(){var n;if(this.chatWindow)return;this.chatWindow=document.createElement("div"),this.chatWindow.className="foisit-chat";const t=document.createElement("div");t.className="foisit-header";const e=document.createElement("span");e.className="foisit-title",e.textContent="Foisit";const i=document.createElement("button");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";const s=document.createElement("div");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=>{var r;if(a.key==="Enter"&&((r=this.input)!=null&&r.value.trim())){const c=this.input.value.trim();this.input.value="",this.onSubmit&&this.onSubmit(c)}}),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)}registerCallbacks(t,e){this.onSubmit=t,this.onClose=e}toggle(t,e){t&&(this.onSubmit=t),e&&(this.onClose=e),this.isOpen=!this.isOpen,this.chatWindow&&(this.isOpen?(this.chatWindow.style.display="flex",requestAnimationFrame(()=>{this.chatWindow&&(this.chatWindow.style.opacity="1",this.chatWindow.style.transform="translateY(0) scale(1)")}),setTimeout(()=>{var i;return(i=this.input)==null?void 0:i.focus()},100)):(this.chatWindow.style.opacity="0",this.chatWindow.style.transform="translateY(20px) scale(0.95)",setTimeout(()=>{this.chatWindow&&!this.isOpen&&(this.chatWindow.style.display="none")},200),this.onClose&&this.onClose()))}addMessage(t,e){if(!this.messagesContainer)return;const i=document.createElement("div");i.textContent=t,i.className=e==="user"?"foisit-bubble user":"foisit-bubble system",this.messagesContainer.appendChild(i),this.scrollToBottom()}addOptions(t){if(!this.messagesContainer)return;const e=document.createElement("div");e.className="foisit-options-container",t.forEach(i=>{const s=document.createElement("button");s.textContent=i.label,s.className="foisit-option-chip",s.setAttribute("type","button"),s.setAttribute("aria-label",i.label);const n=()=>{if(i.commandId){this.onSubmit&&this.onSubmit({commandId:i.commandId});return}const a=i&&typeof i.value=="string"&&i.value.trim()?i.value:i.label;this.onSubmit&&this.onSubmit(a)};s.onclick=n,s.onkeydown=a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),n())},e.appendChild(s)}),this.messagesContainer.appendChild(e),this.scrollToBottom()}addForm(t,e,i){if(!this.messagesContainer)return;this.addMessage(t,"system");const s=document.createElement("form");s.className="foisit-form";const n=[],a=(o,p)=>{const b=document.createElement("div");return b.className="foisit-form-label",b.innerHTML=o+(p?' <span class="foisit-req-star">*</span>':""),b},r=()=>{const o=document.createElement("div");return o.className="foisit-form-error",o.style.display="none",o};(e??[]).forEach(o=>{const p=document.createElement("div");p.className="foisit-form-group";const b=o.description||o.name;p.appendChild(a(b,o.required));let h;if(o.type==="select"){const l=document.createElement("select");l.className="foisit-form-input";const y=document.createElement("option");y.value="",y.textContent="Select...",l.appendChild(y);const x=m=>{(m??[]).forEach(w=>{const g=document.createElement("option");g.value=String(w.value??w.label??""),g.textContent=String(w.label??w.value??""),l.appendChild(g)})};if(Array.isArray(o.options)&&o.options.length)x(o.options);else if(typeof o.getOptions=="function"){const m=o.getOptions,w=document.createElement("option");w.value="",w.textContent="Loading...",l.appendChild(w),Promise.resolve().then(()=>m()).then(g=>{for(;l.options.length>1;)l.remove(1);x(g)}).catch(()=>{for(;l.options.length>1;)l.remove(1);const g=document.createElement("option");g.value="",g.textContent="Error loading options",l.appendChild(g)})}o.defaultValue!=null&&(l.value=String(o.defaultValue)),h=l}else if(o.type==="file"){const l=o,y=document.createElement("input");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()=>{const x=Array.from(y.files||[]),m=C;if(m.style.display="none",m.textContent="",x.length===0)return;const w=l.maxFiles??(l.multiple?10:1);if(x.length>w){m.textContent=`Please select at most ${w} file(s).`,m.style.display="block";return}const g=l.maxSizeBytes??1/0,u=x.reduce((v,E)=>v+E.size,0);if(x.some(v=>v.size>g)){m.textContent=`One or more files exceed the maximum size of ${Math.round(g/1024)} KB.`,m.style.display="block";return}const k=l.maxTotalBytes??1/0;if(u>k){m.textContent=`Total selected files exceed the maximum of ${Math.round(k/1024)} KB.`,m.style.display="block";return}if(l.accept&&Array.isArray(l.accept)){const v=l.accept;if(!x.every(L=>L.type?v.some(I=>I.startsWith(".")?L.name.toLowerCase().endsWith(I.toLowerCase()):L.type===I||L.type.startsWith(I.split("/")[0]+"/")):!0)){m.textContent="One or more files have an unsupported type.",m.style.display="block";return}}}),h=y}else{const l=document.createElement("input");l.className="foisit-form-input",o.type==="string"&&(l.placeholder=o.placeholder||"Type here..."),o.type==="number"?(l.type="number",typeof o.min=="number"&&(l.min=String(o.min)),typeof o.max=="number"&&(l.max=String(o.max)),typeof o.step=="number"&&(l.step=String(o.step)),o.defaultValue!=null&&(l.value=String(o.defaultValue))):o.type==="date"?(l.type="date",typeof o.min=="string"&&(l.min=o.min),typeof o.max=="string"&&(l.max=o.max),o.defaultValue!=null&&(l.value=String(o.defaultValue))):(l.type="text",o.defaultValue!=null&&(l.value=String(o.defaultValue))),h=l}const C=r();p.appendChild(h),p.appendChild(C),n.push({name:o.name,type:o.type,el:h,required:o.required}),s.appendChild(p)});const c=document.createElement("div");c.className="foisit-form-actions";const f=document.createElement("button");f.type="submit",f.textContent="Submit",f.className="foisit-option-chip",f.style.fontWeight="600",c.appendChild(f),s.appendChild(c),s.onsubmit=async o=>{o.preventDefault();const p={};let b=!1;s.querySelectorAll(".foisit-form-error").forEach(h=>{h.style.display="none",h.textContent=""}),s.querySelectorAll(".foisit-form-input").forEach(h=>{h.classList.remove("foisit-error-border")});for(const h of n){if(h.type==="file"){const x=h.el.parentElement,m=x==null?void 0:x.querySelector(".foisit-form-error"),w=h.el,g=Array.from(w.files||[]);if(h.required&&g.length===0){b=!0,w.classList.add("foisit-error-border"),m&&(m.textContent="This file is required",m.style.display="block");continue}if(g.length===0)continue;const u=(e??[]).find(v=>v.name===h.name),k=(u==null?void 0:u.delivery)??"file";if(u!=null&&u.maxWidth||u!=null&&u.maxHeight)try{const v=await this.getImageDimensions(g[0]);if(u.maxWidth&&v.width>u.maxWidth){b=!0,m&&(m.textContent=`Image width must be ≤ ${u.maxWidth}px`,m.style.display="block");continue}if(u.maxHeight&&v.height>u.maxHeight){b=!0,m&&(m.textContent=`Image height must be ≤ ${u.maxHeight}px`,m.style.display="block");continue}}catch{}if(u!=null&&u.maxDurationSec)try{const v=await this.getMediaDuration(g[0]);if(v&&v>u.maxDurationSec){b=!0,m&&(m.textContent=`Media duration must be ≤ ${u.maxDurationSec}s`,m.style.display="block");continue}}catch{}if(k==="file")p[h.name]=u!=null&&u.multiple?g:g[0];else if(k==="base64")try{const v=await Promise.all(g.map(E=>this.readFileAsDataURL(E)));p[h.name]=u!=null&&u.multiple?v:v[0]}catch{b=!0,m&&(m.textContent="Failed to encode file(s) to base64.",m.style.display="block");continue}continue}const C=(h.el.value??"").toString().trim(),l=h.el.parentElement,y=l==null?void 0:l.querySelector(".foisit-form-error");if(h.required&&(C==null||C==="")){b=!0,h.el.classList.add("foisit-error-border"),y&&(y.textContent="This field is required",y.style.display="block");continue}if(C!=="")if(h.type==="number"){const x=Number(C);Number.isNaN(x)||(p[h.name]=x)}else p[h.name]=C}if(b){s.classList.add("foisit-shake"),setTimeout(()=>s.classList.remove("foisit-shake"),400);return}f.disabled=!0,f.style.opacity="0.6",n.forEach(h=>{h.el.disabled=!0}),i(p)},this.messagesContainer.appendChild(s),this.scrollToBottom()}showLoading(){if(this.messagesContainer&&!this.loadingEl){this.loadingEl=document.createElement("div"),this.loadingEl.className="foisit-loading-dots foisit-bubble system";for(let t=0;t<3;t++){const e=document.createElement("div");e.className="foisit-dot",e.style.animation=`foisitPulse 1.4s infinite ease-in-out ${t*.2}s`,this.loadingEl.appendChild(e)}this.messagesContainer.appendChild(this.loadingEl),this.scrollToBottom()}}hideLoading(){var t;(t=this.loadingEl)==null||t.remove(),this.loadingEl=null}scrollToBottom(){this.messagesContainer&&(this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight)}destroy(){var t;(t=this.container)==null||t.remove(),this.container=null,this.chatWindow=null,this.messagesContainer=null,this.input=null,this.isOpen=!1}readFileAsDataURL(t){return new Promise((e,i)=>{const s=new FileReader;s.onerror=()=>i(new Error("Failed to read file")),s.onload=()=>e(String(s.result)),s.readAsDataURL(t)})}getImageDimensions(t){return new Promise(e=>{try{const i=URL.createObjectURL(t),s=new Image;s.onload=()=>{const n={width:s.naturalWidth||s.width,height:s.naturalHeight||s.height};URL.revokeObjectURL(i),e(n)},s.onerror=()=>{URL.revokeObjectURL(i),e({width:0,height:0})},s.src=i}catch{e({width:0,height:0})}})}getMediaDuration(t){return new Promise(e=>{try{const i=URL.createObjectURL(t),s=t.type.startsWith("audio")?document.createElement("audio"):document.createElement("video");let n=!1;const a=setTimeout(()=>{n||(n=!0,URL.revokeObjectURL(i),e(0))},5e3);s.preload="metadata",s.onloadedmetadata=()=>{if(n)return;n=!0,clearTimeout(a);const c=s.duration||0;URL.revokeObjectURL(i),e(c)},s.onerror=()=>{n||(n=!0,clearTimeout(a),URL.revokeObjectURL(i),e(0))},s.src=i}catch{e(0)}})}injectOverlayStyles(){if(document.getElementById("foisit-overlay-styles"))return;const t=document.createElement("style");t.id="foisit-overlay-styles",t.textContent=`
34
+ `,document.head.appendChild(t),console.log("Gradient styles injected")}function D(){if(document.querySelector("#gradient-indicator"))return;const d=document.createElement("div");d.id="gradient-indicator",U(),d.classList.add("gradient-indicator"),document.body.appendChild(d),console.log("Gradient indicator added to the DOM")}function $(){const d=document.querySelector("#gradient-indicator");d&&(d.remove(),console.log("Gradient indicator removed from the DOM"))}function V(){return typeof window<"u"&&typeof document<"u"}class G{constructor(){this.state="idle",this.subscribers=[]}getState(){return this.state}setState(t){this.state=t,this.notifySubscribers(),console.log("State updated:",t),t==="listening"?D():$()}subscribe(t){this.subscribers.push(t)}notifySubscribers(){this.subscribers.forEach(t=>t(this.state))}}class Y{constructor(t){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()}init(){var e,i;if(this.container)return;this.injectOverlayStyles();const t=document.getElementById("foisit-overlay-container");if(t&&t instanceof HTMLElement){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();return}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()}renderFloatingButton(){var s,n,o,r,c,f;const t=document.createElement("button");t.innerHTML=((s=this.config.floatingButton)==null?void 0:s.customHtml)||"🎙️";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";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)}renderChatWindow(){var n;if(this.chatWindow)return;this.chatWindow=document.createElement("div"),this.chatWindow.className="foisit-chat";const t=document.createElement("div");t.className="foisit-header";const e=document.createElement("span");e.className="foisit-title",e.textContent="Foisit";const i=document.createElement("button");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";const s=document.createElement("div");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=>{var r;if(o.key==="Enter"&&((r=this.input)!=null&&r.value.trim())){const c=this.input.value.trim();this.input.value="",this.onSubmit&&this.onSubmit(c)}}),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)}registerCallbacks(t,e){this.active&&(this.onSubmit=t,this.onClose=e)}toggle(t,e){this.active&&(t&&(this.onSubmit=t),e&&(this.onClose=e),this.isOpen=!this.isOpen,this.chatWindow&&(this.isOpen?(this.chatWindow.style.display="flex",requestAnimationFrame(()=>{this.chatWindow&&(this.chatWindow.style.opacity="1",this.chatWindow.style.transform="translateY(0) scale(1)")}),setTimeout(()=>{var i;return(i=this.input)==null?void 0:i.focus()},100)):(this.chatWindow.style.opacity="0",this.chatWindow.style.transform="translateY(20px) scale(0.95)",setTimeout(()=>{this.chatWindow&&!this.isOpen&&(this.chatWindow.style.display="none")},200),this.onClose&&this.onClose())))}addMessage(t,e){if(!this.messagesContainer)return;const i=document.createElement("div");i.textContent=t,i.className=e==="user"?"foisit-bubble user":"foisit-bubble system",this.messagesContainer.appendChild(i),this.scrollToBottom()}addOptions(t){if(!this.messagesContainer)return;const e=document.createElement("div");e.className="foisit-options-container",t.forEach(i=>{const s=document.createElement("button");s.textContent=i.label,s.className="foisit-option-chip",s.setAttribute("type","button"),s.setAttribute("aria-label",i.label);const n=()=>{if(i.commandId){this.onSubmit&&this.onSubmit({commandId:i.commandId});return}const o=i&&typeof i.value=="string"&&i.value.trim()?i.value:i.label;this.onSubmit&&this.onSubmit(o)};s.onclick=n,s.onkeydown=o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),n())},e.appendChild(s)}),this.messagesContainer.appendChild(e),this.scrollToBottom()}addForm(t,e,i){if(!this.messagesContainer)return;this.addMessage(t,"system");const s=document.createElement("form");s.className="foisit-form";const n=[],o=(a,p)=>{const g=document.createElement("div");return g.className="foisit-form-label",g.innerHTML=a+(p?' <span class="foisit-req-star">*</span>':""),g},r=()=>{const a=document.createElement("div");return a.className="foisit-form-error",a.style.display="none",a};(e??[]).forEach(a=>{const p=document.createElement("div");p.className="foisit-form-group";const g=a.description||a.name;p.appendChild(o(g,a.required));let m;if(a.type==="select"){const l=document.createElement("select");l.className="foisit-form-input";const b=document.createElement("option");b.value="",b.textContent="Select...",l.appendChild(b);const x=h=>{(h??[]).forEach(w=>{const y=document.createElement("option");y.value=String(w.value??w.label??""),y.textContent=String(w.label??w.value??""),l.appendChild(y)})};if(Array.isArray(a.options)&&a.options.length)x(a.options);else if(typeof a.getOptions=="function"){const h=a.getOptions,w=document.createElement("option");w.value="",w.textContent="Loading...",l.appendChild(w),Promise.resolve().then(()=>h()).then(y=>{for(;l.options.length>1;)l.remove(1);x(y)}).catch(()=>{for(;l.options.length>1;)l.remove(1);const y=document.createElement("option");y.value="",y.textContent="Error loading options",l.appendChild(y)})}a.defaultValue!=null&&(l.value=String(a.defaultValue)),m=l}else if(a.type==="file"){const l=a,b=document.createElement("input");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()=>{const x=Array.from(b.files||[]),h=S;if(h.style.display="none",h.textContent="",x.length===0)return;const w=l.maxFiles??(l.multiple?10:1);if(x.length>w){h.textContent=`Please select at most ${w} file(s).`,h.style.display="block";return}const y=l.maxSizeBytes??1/0,u=x.reduce((v,E)=>v+E.size,0);if(x.some(v=>v.size>y)){h.textContent=`One or more files exceed the maximum size of ${Math.round(y/1024)} KB.`,h.style.display="block";return}const k=l.maxTotalBytes??1/0;if(u>k){h.textContent=`Total selected files exceed the maximum of ${Math.round(k/1024)} KB.`,h.style.display="block";return}if(l.accept&&Array.isArray(l.accept)){const v=l.accept;if(!x.every(L=>L.type?v.some(I=>I.startsWith(".")?L.name.toLowerCase().endsWith(I.toLowerCase()):L.type===I||L.type.startsWith(I.split("/")[0]+"/")):!0)){h.textContent="One or more files have an unsupported type.",h.style.display="block";return}}}),m=b}else{const l=document.createElement("input");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}const S=r();p.appendChild(m),p.appendChild(S),n.push({name:a.name,type:a.type,el:m,required:a.required}),s.appendChild(p)});const c=document.createElement("div");c.className="foisit-form-actions";const f=document.createElement("button");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=>{a.preventDefault();const p={};let g=!1;s.querySelectorAll(".foisit-form-error").forEach(m=>{m.style.display="none",m.textContent=""}),s.querySelectorAll(".foisit-form-input").forEach(m=>{m.classList.remove("foisit-error-border")});for(const m of n){if(m.type==="file"){const x=m.el.parentElement,h=x==null?void 0:x.querySelector(".foisit-form-error"),w=m.el,y=Array.from(w.files||[]);if(m.required&&y.length===0){g=!0,w.classList.add("foisit-error-border"),h&&(h.textContent="This file is required",h.style.display="block");continue}if(y.length===0)continue;const u=(e??[]).find(v=>v.name===m.name),k=(u==null?void 0:u.delivery)??"file";if(u!=null&&u.maxWidth||u!=null&&u.maxHeight)try{const v=await this.getImageDimensions(y[0]);if(u.maxWidth&&v.width>u.maxWidth){g=!0,h&&(h.textContent=`Image width must be ≤ ${u.maxWidth}px`,h.style.display="block");continue}if(u.maxHeight&&v.height>u.maxHeight){g=!0,h&&(h.textContent=`Image height must be ≤ ${u.maxHeight}px`,h.style.display="block");continue}}catch{}if(u!=null&&u.maxDurationSec)try{const v=await this.getMediaDuration(y[0]);if(v&&v>u.maxDurationSec){g=!0,h&&(h.textContent=`Media duration must be ≤ ${u.maxDurationSec}s`,h.style.display="block");continue}}catch{}if(k==="file")p[m.name]=u!=null&&u.multiple?y:y[0];else if(k==="base64")try{const v=await Promise.all(y.map(E=>this.readFileAsDataURL(E)));p[m.name]=u!=null&&u.multiple?v:v[0]}catch{g=!0,h&&(h.textContent="Failed to encode file(s) to base64.",h.style.display="block");continue}continue}const S=(m.el.value??"").toString().trim(),l=m.el.parentElement,b=l==null?void 0:l.querySelector(".foisit-form-error");if(m.required&&(S==null||S==="")){g=!0,m.el.classList.add("foisit-error-border"),b&&(b.textContent="This field is required",b.style.display="block");continue}if(S!=="")if(m.type==="number"){const x=Number(S);Number.isNaN(x)||(p[m.name]=x)}else p[m.name]=S}if(g){s.classList.add("foisit-shake"),setTimeout(()=>s.classList.remove("foisit-shake"),400);return}f.disabled=!0,f.style.opacity="0.6",n.forEach(m=>{m.el.disabled=!0}),i(p)},this.messagesContainer.appendChild(s),this.scrollToBottom()}showLoading(){if(this.messagesContainer&&!this.loadingEl){this.loadingEl=document.createElement("div"),this.loadingEl.className="foisit-loading-dots foisit-bubble system";for(let t=0;t<3;t++){const e=document.createElement("div");e.className="foisit-dot",e.style.animation=`foisitPulse 1.4s infinite ease-in-out ${t*.2}s`,this.loadingEl.appendChild(e)}this.messagesContainer.appendChild(this.loadingEl),this.scrollToBottom()}}hideLoading(){var t;(t=this.loadingEl)==null||t.remove(),this.loadingEl=null}scrollToBottom(){this.messagesContainer&&(this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight)}destroy(){var t;(t=this.container)==null||t.remove(),this.container=null,this.chatWindow=null,this.messagesContainer=null,this.input=null,this.isOpen=!1}readFileAsDataURL(t){return new Promise((e,i)=>{const s=new FileReader;s.onerror=()=>i(new Error("Failed to read file")),s.onload=()=>e(String(s.result)),s.readAsDataURL(t)})}getImageDimensions(t){return new Promise(e=>{try{const i=URL.createObjectURL(t),s=new Image;s.onload=()=>{const n={width:s.naturalWidth||s.width,height:s.naturalHeight||s.height};URL.revokeObjectURL(i),e(n)},s.onerror=()=>{URL.revokeObjectURL(i),e({width:0,height:0})},s.src=i}catch{e({width:0,height:0})}})}getMediaDuration(t){return new Promise(e=>{try{const i=URL.createObjectURL(t),s=t.type.startsWith("audio")?document.createElement("audio"):document.createElement("video");let n=!1;const o=setTimeout(()=>{n||(n=!0,URL.revokeObjectURL(i),e(0))},5e3);s.preload="metadata",s.onloadedmetadata=()=>{if(n)return;n=!0,clearTimeout(o);const c=s.duration||0;URL.revokeObjectURL(i),e(c)},s.onerror=()=>{n||(n=!0,clearTimeout(o),URL.revokeObjectURL(i),e(0))},s.src=i}catch{e(0)}})}injectOverlayStyles(){if(document.getElementById("foisit-overlay-styles"))return;const t=document.createElement("style");t.id="foisit-overlay-styles",t.textContent=`
35
35
  :root {
36
36
  /* LIGHT MODE (Default) - Smoother gradient */
37
37
  /* Changed: Softer, right-focused radial highlight to avoid a heavy white bottom */
@@ -352,4 +352,4 @@
352
352
  transition: transform 0.2s;
353
353
  }
354
354
  .foisit-floating-btn:hover { transform: scale(1.05); }
355
- `,document.head.appendChild(t)}}class H{constructor(t){this.config=t,this.isActivated=!1,this.lastProcessedInput="",this.processingLock=!1,this.defaultIntroMessage="How can I help you?",this.commandHandler=new B({enableSmartIntent:this.config.enableSmartIntent!==!1,intentEndpoint:this.config.intentEndpoint}),this.fallbackHandler=new O,this.voiceProcessor=new z,this.textToSpeech=new q,this.stateManager=new V,this.gestureHandler=new j,this.overlayManager=new G({floatingButton:this.config.floatingButton,inputPlaceholder:this.config.inputPlaceholder}),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(async e=>{if(typeof e=="string"){this.overlayManager.addMessage(e,"user"),await this.handleCommand(e);return}if(e&&typeof e=="object"){const i=e,s=i.label??i.commandId??"Selection";this.overlayManager.addMessage(String(s),"user"),this.overlayManager.showLoading();const n=await this.commandHandler.executeCommand(i);this.overlayManager.hideLoading(),this.processResponse(n)}},()=>console.log("AssistantService: Overlay closed."))}startListening(){console.log("AssistantService: Voice is disabled; startListening() is a no-op.")}stopListening(){console.log("AssistantService: Voice is disabled; stopListening() is a no-op."),this.isActivated=!1}reactivate(){console.log("AssistantService: Reactivating assistant..."),this.isActivated=!1;try{this.startListening()}catch{}}async processActivation(t){var i;const e=(i=this.config.activationCommand)==null?void 0:i.toLowerCase();e&&(t===e?(console.log("AssistantService: Activation matched."),this.isActivated=!0,this.textToSpeech.speak(this.config.introMessage||this.defaultIntroMessage)):console.log("AssistantService: Activation command not recognized."))}async handleCommand(t){this.overlayManager.showLoading();let e;try{e=await this.commandHandler.executeCommand(t)}finally{this.overlayManager.hideLoading()}if(e.type==="form"&&e.fields){this.overlayManager.addForm(e.message,e.fields,async i=>{this.overlayManager.showLoading();let s;try{s=await this.commandHandler.executeCommand(i)}finally{this.overlayManager.hideLoading()}this.processResponse(s)});return}if(e.type==="error"){this.fallbackHandler.handleFallback(t),this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(),"system");return}e.message?this.overlayManager.addMessage(e.message,"system"):(e.type==="ambiguous"||e.type==="confirm")&&e.options&&this.overlayManager.addOptions(e.options)}destroy(){this.voiceProcessor.stopListening(),this.gestureHandler.destroy(),this.overlayManager.destroy()}processResponse(t){if(t){if(t.type==="form"&&t.fields){this.overlayManager.addForm(t.message,t.fields,async e=>{this.overlayManager.showLoading();let i;try{i=await this.commandHandler.executeCommand(e)}finally{this.overlayManager.hideLoading()}this.processResponse(i)});return}if((t.type==="ambiguous"||t.type==="confirm")&&t.options){t.message&&this.overlayManager.addMessage(t.message,"system"),this.overlayManager.addOptions(t.options);return}t.message&&this.overlayManager.addMessage(t.message,"system")}}addCommand(t,e){console.log(typeof t=="string"?`AssistantService: Adding command "${t}".`:`AssistantService: Adding rich command "${t.command}".`),this.commandHandler.addCommand(t,e)}removeCommand(t){console.log(`AssistantService: Removing command "${t}".`),this.commandHandler.removeCommand(t)}getCommands(){return this.commandHandler.getCommands()}toggle(t,e){console.log("AssistantService: Toggling overlay..."),this.overlayManager.toggle(async i=>{if(typeof i=="string"){this.overlayManager.addMessage(i,"user"),t&&t(i),await this.handleCommand(i);return}if(i&&typeof i=="object"){const s=i,n=s.label??s.commandId??"Selection";this.overlayManager.addMessage(String(n),"user"),this.overlayManager.showLoading();let a;try{a=await this.commandHandler.executeCommand(s)}finally{this.overlayManager.hideLoading()}this.processResponse(a)}},()=>{console.log("AssistantService: Overlay closed."),e&&e()})}}const M=S.createContext(null);let R=null;const Y=({config:d,children:t})=>{const[e,i]=S.useState(null),[s,n]=S.useState(!1);return S.useEffect(()=>{R?console.warn("Multiple AssistantProvider instances detected. Reusing global AssistantService."):(console.log("Initializing global AssistantService..."),R=new H(d));const a=R;return i(a),n(!0),()=>{var r;console.log("Cleaning up AssistantService..."),(r=a.destroy)==null||r.call(a),R=null}},[d]),!s||!e?A.jsx("div",{children:"Loading Assistant..."}):A.jsx(M.Provider,{value:e,children:t})},F=()=>{const d=S.useContext(M);if(console.log("assistant",d),!d)throw new Error("useAssistant must be used within an AssistantProvider");return d},K=({label:d="Activate Assistant",onActivate:t})=>{const e=F(),i=()=>{t&&t(),e.reactivate()};return A.jsx("button",{onClick:i,className:"assistant-activator",children:d})},X=d=>{const[t,e]=S.useState(d.getState());return S.useEffect(()=>{const i=s=>{e(s)};return d.subscribe(i),()=>{d.subscribe(()=>{})}},[d]),t};exports.AssistantActivator=K;exports.AssistantContext=M;exports.AssistantProvider=Y;exports.AssistantService=H;exports.ReactWrapper=N;exports.useAssistant=F;exports.useAssistantState=X;
355
+ `,document.head.appendChild(t)}}class q{constructor(t){this.config=t,this.isActivated=!1,this.lastProcessedInput="",this.processingLock=!1,this.defaultIntroMessage="How can I help you?",this.commandHandler=new B({enableSmartIntent:this.config.enableSmartIntent!==!1,intentEndpoint:this.config.intentEndpoint}),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 j,this.overlayManager=new Y({floatingButton:this.config.floatingButton,inputPlaceholder:this.config.inputPlaceholder}),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(async e=>{if(typeof e=="string"){this.overlayManager.addMessage(e,"user"),await this.handleCommand(e);return}if(e&&typeof e=="object"){const i=e,s=i.label??i.commandId??"Selection";this.overlayManager.addMessage(String(s),"user"),this.overlayManager.showLoading();const n=await this.commandHandler.executeCommand(i);this.overlayManager.hideLoading(),this.processResponse(n)}},()=>console.log("AssistantService: Overlay closed."))):(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))}startListening(){if(typeof window>"u"||!this.voiceProcessor){console.log("AssistantService: Voice is disabled or unavailable; startListening() is a no-op.");return}}stopListening(){if(typeof window>"u"||!this.voiceProcessor){console.log("AssistantService: Voice unavailable; stopListening() is a no-op."),this.isActivated=!1;return}this.voiceProcessor.stopListening(),this.isActivated=!1}reactivate(){console.log("AssistantService: Reactivating assistant..."),this.isActivated=!1;try{this.startListening()}catch{}}async processActivation(t){var i;const e=(i=this.config.activationCommand)==null?void 0:i.toLowerCase();e&&(t===e?(console.log("AssistantService: Activation matched."),this.isActivated=!0,this.textToSpeech.speak(this.config.introMessage||this.defaultIntroMessage)):console.log("AssistantService: Activation command not recognized."))}async handleCommand(t){this.overlayManager.showLoading();let e;try{e=await this.commandHandler.executeCommand(t)}finally{this.overlayManager.hideLoading()}if(e.type==="form"&&e.fields){this.overlayManager.addForm(e.message,e.fields,async i=>{this.overlayManager.showLoading();let s;try{s=await this.commandHandler.executeCommand(i)}finally{this.overlayManager.hideLoading()}this.processResponse(s)});return}if(e.type==="error"){this.fallbackHandler.handleFallback(t),this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(),"system");return}if((e.type==="ambiguous"||e.type==="confirm")&&e.options){e.message&&this.overlayManager.addMessage(e.message,"system"),this.overlayManager.addOptions(e.options);return}e.message&&this.overlayManager.addMessage(e.message,"system")}destroy(){this.voiceProcessor.stopListening(),this.gestureHandler.destroy(),this.overlayManager.destroy()}processResponse(t){if(t){if(t.type==="form"&&t.fields){this.overlayManager.addForm(t.message,t.fields,async e=>{this.overlayManager.showLoading();let i;try{i=await this.commandHandler.executeCommand(e)}finally{this.overlayManager.hideLoading()}this.processResponse(i)});return}if((t.type==="ambiguous"||t.type==="confirm")&&t.options){t.message&&this.overlayManager.addMessage(t.message,"system"),this.overlayManager.addOptions(t.options);return}t.message&&this.overlayManager.addMessage(t.message,"system")}}addCommand(t,e){console.log(typeof t=="string"?`AssistantService: Adding command "${t}".`:`AssistantService: Adding rich command "${t.command}".`),this.commandHandler.addCommand(t,e)}removeCommand(t){console.log(`AssistantService: Removing command "${t}".`),this.commandHandler.removeCommand(t)}getCommands(){return this.commandHandler.getCommands()}toggle(t,e){console.log("AssistantService: Toggling overlay..."),this.overlayManager.toggle(async i=>{if(typeof i=="string"){this.overlayManager.addMessage(i,"user"),t&&t(i),await this.handleCommand(i);return}if(i&&typeof i=="object"){const s=i,n=s.label??s.commandId??"Selection";this.overlayManager.addMessage(String(n),"user"),this.overlayManager.showLoading();let o;try{o=await this.commandHandler.executeCommand(s)}finally{this.overlayManager.hideLoading()}this.processResponse(o)}},()=>{console.log("AssistantService: Overlay closed."),e&&e()})}}const M=C.createContext(null);let R=null;const K=({config:d,children:t})=>{const[e,i]=C.useState(null),[s,n]=C.useState(!1);return C.useEffect(()=>{R?console.warn("Multiple AssistantProvider instances detected. Reusing global AssistantService."):(console.log("Initializing global AssistantService..."),R=new q(d));const o=R;return i(o),n(!0),()=>{var r;console.log("Cleaning up AssistantService..."),(r=o.destroy)==null||r.call(o),R=null}},[d]),!s||!e?A.jsx("div",{children:"Loading Assistant..."}):A.jsx(M.Provider,{value:e,children:t})},F=()=>{const d=C.useContext(M);if(console.log("assistant",d),!d)throw new Error("useAssistant must be used within an AssistantProvider");return d},X=({label:d="Activate Assistant",onActivate:t})=>{const e=F(),i=()=>{t&&t(),e.reactivate()};return A.jsx("button",{onClick:i,className:"assistant-activator",children:d})},_=d=>{const[t,e]=C.useState(d.getState());return C.useEffect(()=>{const i=s=>{e(s)};return d.subscribe(i),()=>{d.subscribe(()=>{})}},[d]),t};exports.AssistantActivator=X;exports.AssistantContext=M;exports.AssistantProvider=K;exports.AssistantService=q;exports.ReactWrapper=W;exports.useAssistant=F;exports.useAssistantState=_;
package/index.mjs CHANGED
@@ -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 ?? {}, o = this.getCommandById(c);
70
- if (!o) return { message: "That command is not available.", type: "error" };
71
- const p = this.sanitizeParamsForCommand(o, f), h = (o.parameters ?? []).filter((C) => C.required).filter((C) => p[C.name] == null || p[C.name] === "");
72
- return h.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(o), params: p }, {
73
- message: `Please provide the required details for "${o.command}".`,
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
74
  type: "form",
75
- fields: h
76
- }) : o.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(o), params: p }, this.buildConfirmResponse(o)) : this.safeRunAction(o, p);
75
+ fields: m
76
+ }) : a.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(a), params: p }, this.buildConfirmResponse(a)) : this.safeRunAction(a, p);
77
77
  }
78
78
  if (!this.context)
79
79
  return { message: "Session expired or invalid context.", type: "error" };
@@ -82,28 +82,28 @@ 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 a = {
85
+ const o = {
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: a
92
+ params: o
93
93
  }, this.buildConfirmResponse(n);
94
- const r = await this.safeRunAction(n, a);
94
+ const r = await this.safeRunAction(n, o);
95
95
  return this.context = null, this.normalizeResponse(r);
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: a, params: r } = this.pendingConfirmation;
101
+ const { commandId: o, params: r } = this.pendingConfirmation;
102
102
  this.pendingConfirmation = null;
103
- const c = this.getCommandById(a);
103
+ const c = this.getCommandById(o);
104
104
  return c ? this.safeRunAction(c, r) : { message: "That action is no longer available.", type: "error" };
105
105
  }
106
- return ["no", "n", "cancel", "stop"].includes(n) ? (this.pendingConfirmation = null, { message: "Cancelled.", type: "success" }) : {
106
+ return ["no", "n", "cancel", "stop"].includes(n) ? (this.pendingConfirmation = null, { message: "Cancelled.", type: "success" }) : {
107
107
  message: "Please confirm: Yes or No.",
108
108
  type: "confirm",
109
109
  options: [
@@ -114,22 +114,22 @@ class B {
114
114
  }
115
115
  const i = this.commands.get(e);
116
116
  if (i) {
117
- const n = i, a = (n.parameters ?? []).filter((r) => r.required);
118
- return a.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
117
+ const n = i, o = (n.parameters ?? []).filter((r) => r.required);
118
+ return o.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
119
119
  message: `Please provide the required details for "${n.command}".`,
120
120
  type: "form",
121
- fields: a
121
+ fields: o
122
122
  }) : n.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(n), params: {} }, this.buildConfirmResponse(n)) : this.safeRunAction(n, {});
123
123
  }
124
124
  const s = await this.tryDeterministicMatch(e);
125
125
  if (s) return s;
126
126
  if (this.enableSmartIntent && this.openAIService) {
127
- const n = await this.getCommandsForAI(), a = await this.openAIService.determineIntent(
127
+ const n = await this.getCommandsForAI(), o = await this.openAIService.determineIntent(
128
128
  e,
129
129
  n,
130
130
  this.context
131
131
  );
132
- return this.handleAIResult(a);
132
+ return this.handleAIResult(o);
133
133
  }
134
134
  return this.enableSmartIntent ? this.listAllCommands() : { message: "I'm not sure what you mean.", type: "error" };
135
135
  }
@@ -138,30 +138,28 @@ class B {
138
138
  const e = this.getCommandById(t.match);
139
139
  if (!e)
140
140
  return { message: "I'm not sure what you mean.", type: "error" };
141
- const i = t.params ?? {}, s = this.sanitizeParamsForCommand(e, i), a = (e.parameters ?? []).filter((c) => c.required).filter((c) => s[c.name] == null || s[c.name] === "");
142
- if (t.incomplete || a.length > 0) {
143
- this.context = { commandId: this.getCommandIdentifier(e), params: s };
144
- const c = a.some((f) => f.type === "select");
145
- if (a.length <= 2 && !c) {
146
- const f = a.map((o) => o.name).join(" and ");
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 ");
147
145
  return {
148
- message: t.message || `Please provide ${f}.`,
146
+ message: t.message || `Please provide ${p}.`,
149
147
  type: "question"
150
148
  };
151
149
  }
152
150
  return {
153
151
  message: t.message || `Please fill in the missing details for "${e.command}".`,
154
152
  type: "form",
155
- fields: a
153
+ fields: r
156
154
  };
157
155
  }
158
156
  if (e.critical)
159
157
  return this.pendingConfirmation = {
160
158
  commandId: this.getCommandIdentifier(e),
161
- params: s
159
+ params: n
162
160
  }, this.buildConfirmResponse(e);
163
- const r = await e.action(s);
164
- return this.normalizeResponse(r);
161
+ const c = await e.action(n);
162
+ return this.normalizeResponse(c);
165
163
  }
166
164
  return t.type === "ambiguous" && t.options && t.options.length ? {
167
165
  message: t.message || "Did you mean one of these?",
@@ -175,14 +173,61 @@ class B {
175
173
  }
176
174
  sanitizeParamsForCommand(t, e) {
177
175
  const i = { ...e ?? {} };
178
- for (const s of t.parameters ?? [])
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
+ }
179
206
  if (s.type === "date") {
180
- const n = i[s.name];
181
- if (typeof n == "string") {
182
- const a = n.trim();
183
- this.isIsoDateString(a) || delete i[s.name];
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;
184
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];
185
229
  }
230
+ }
186
231
  return i;
187
232
  }
188
233
  isIsoDateString(t) {
@@ -190,9 +235,14 @@ class B {
190
235
  const e = /* @__PURE__ */ new Date(`${t}T00:00:00Z`);
191
236
  return !Number.isNaN(e.getTime());
192
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
+ }
193
243
  buildConfirmResponse(t) {
194
244
  return {
195
- message: `⚠️ Are you sure you want to run "${t.command}"?`,
245
+ message: `Are you sure you want to run "${t.command}"?`,
196
246
  type: "confirm",
197
247
  options: [
198
248
  { label: "Yes", value: "yes" },
@@ -206,10 +256,10 @@ class B {
206
256
  let c = 0;
207
257
  const f = r.command.toLowerCase();
208
258
  t.includes(f) && (c += 5);
209
- const o = r.keywords ?? [];
210
- for (const p of o) {
211
- const b = p.toLowerCase().trim();
212
- b && (t === b ? c += 4 : t.includes(b) && (c += 3));
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));
213
263
  }
214
264
  c > 0 && e.push({ cmd: r, score: c });
215
265
  }
@@ -226,11 +276,11 @@ class B {
226
276
  commandId: r.cmd.id
227
277
  }))
228
278
  };
229
- const n = s[0].cmd, a = (n.parameters ?? []).filter((r) => r.required);
230
- return a.length > 0 ? (this.context = { commandId: this.getCommandIdentifier(n), params: {} }, {
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: {} }, {
231
281
  message: `Please provide the required details for "${n.command}".`,
232
282
  type: "form",
233
- fields: a
283
+ fields: o
234
284
  }) : n.critical ? (this.pendingConfirmation = { commandId: this.getCommandIdentifier(n), params: {} }, this.buildConfirmResponse(n)) : this.safeRunAction(n, {});
235
285
  }
236
286
  async safeRunAction(t, e) {
@@ -251,14 +301,14 @@ class B {
251
301
  e.parameters && await Promise.all(
252
302
  e.parameters.map(async (i) => {
253
303
  if (i.type !== "select" || !i.getOptions || i.options && i.options.length) return;
254
- const s = `${e.id ?? e.command}:${i.name}`, n = this.selectOptionsCache.get(s), a = Date.now();
255
- if (n && a - n.ts < 6e4) {
304
+ const s = `${e.id ?? e.command}:${i.name}`, n = this.selectOptionsCache.get(s), o = Date.now();
305
+ if (n && o - n.ts < 6e4) {
256
306
  i.options = n.options;
257
307
  return;
258
308
  }
259
309
  try {
260
310
  const r = await i.getOptions();
261
- this.selectOptionsCache.set(s, { options: r, ts: a }), i.options = r;
311
+ this.selectOptionsCache.set(s, { options: r, ts: o }), i.options = r;
262
312
  } catch {
263
313
  }
264
314
  })
@@ -297,19 +347,19 @@ class B {
297
347
  }
298
348
  class H {
299
349
  constructor() {
300
- this.synth = window.speechSynthesis;
350
+ this.synth = typeof window < "u" ? window.speechSynthesis : null;
301
351
  }
302
352
  speak(t, e) {
303
353
  if (!this.synth) {
304
- console.error("SpeechSynthesis API is not supported in this browser.");
354
+ console.error("SpeechSynthesis API is not supported in this environment.");
305
355
  return;
306
356
  }
307
357
  const i = new SpeechSynthesisUtterance(t);
308
- e && (i.pitch = e.pitch || 1, i.rate = e.rate || 1, i.volume = e.volume || 1), i.onstart = () => {
358
+ e && (i.pitch = e.pitch || 1, i.rate = e.rate || 1, i.volume = e.volume || 1), typeof window < "u" && (i.onstart = () => {
309
359
  window.dispatchEvent(new CustomEvent("foisit:tts-start"));
310
360
  }, i.onend = () => {
311
361
  console.log("Speech finished."), window.dispatchEvent(new CustomEvent("foisit:tts-end"));
312
- }, i.onerror = (s) => {
362
+ }), i.onerror = (s) => {
313
363
  console.error("Error during speech synthesis:", s.error);
314
364
  }, this.synth.speak(i);
315
365
  }
@@ -317,7 +367,7 @@ class H {
317
367
  this.synth && this.synth.cancel();
318
368
  }
319
369
  }
320
- class O {
370
+ class z {
321
371
  constructor() {
322
372
  this.fallbackMessage = "Sorry, I didn’t understand that.";
323
373
  }
@@ -332,10 +382,11 @@ class O {
332
382
  }
333
383
  }
334
384
  const T = () => {
385
+ if (typeof window > "u") return null;
335
386
  const d = window;
336
387
  return d.SpeechRecognition ?? d.webkitSpeechRecognition ?? null;
337
388
  };
338
- class z {
389
+ class O {
339
390
  constructor(t = "en-US", e = {}) {
340
391
  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 = () => {
341
392
  var s;
@@ -357,16 +408,16 @@ class z {
357
408
  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);
358
409
  } else
359
410
  this.recognition = null, this.emitStatus("unsupported");
360
- window.addEventListener("foisit:tts-start", this.onTTSStart), window.addEventListener("foisit:tts-end", this.onTTSEnd), this.visibilityHandler = () => {
411
+ typeof window < "u" ? (window.addEventListener("foisit:tts-start", this.onTTSStart), window.addEventListener("foisit:tts-end", this.onTTSEnd), this.visibilityHandler = () => {
361
412
  var s;
362
- if (document.hidden) {
413
+ if (typeof document < "u" && document.hidden) {
363
414
  try {
364
415
  (s = this.recognition) == null || s.stop();
365
416
  } catch {
366
417
  }
367
418
  this.emitStatus(this.ttsSpeaking ? "speaking" : "idle");
368
419
  } else this.isListening && !this.ttsSpeaking && this.safeRestart();
369
- }, document.addEventListener("visibilitychange", this.visibilityHandler);
420
+ }, typeof document < "u" && document.addEventListener("visibilitychange", this.visibilityHandler)) : this.visibilityHandler = void 0;
370
421
  }
371
422
  // Debug logger helpers
372
423
  log(t) {
@@ -418,9 +469,9 @@ class z {
418
469
  var s, n;
419
470
  if (!this.resultCallback) return;
420
471
  const i = e.confidenceThreshold ?? 0.6;
421
- for (let a = t.resultIndex; a < t.results.length; a++) {
422
- const r = t.results[a], c = r && r[0], f = ((n = (s = c == null ? void 0 : c.transcript) == null ? void 0 : s.trim) == null ? void 0 : n.call(s)) || "", o = (c == null ? void 0 : c.confidence) ?? 0;
423
- if (f && !(!r.isFinal && e.interimResults === !1) && !(r.isFinal && o < i))
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))
424
475
  try {
425
476
  this.hadResultThisSession = !0, this.resultCallback(f, !!r.isFinal);
426
477
  } catch {
@@ -501,7 +552,7 @@ class z {
501
552
  }
502
553
  }
503
554
  }
504
- class $ {
555
+ class U {
505
556
  constructor() {
506
557
  this.lastTap = 0;
507
558
  }
@@ -562,17 +613,20 @@ function D() {
562
613
  }
563
614
  `, document.head.appendChild(t), console.log("Gradient styles injected");
564
615
  }
565
- function j() {
616
+ function $() {
566
617
  if (document.querySelector("#gradient-indicator"))
567
618
  return;
568
619
  const d = document.createElement("div");
569
620
  d.id = "gradient-indicator", D(), d.classList.add("gradient-indicator"), document.body.appendChild(d), console.log("Gradient indicator added to the DOM");
570
621
  }
571
- function U() {
622
+ function j() {
572
623
  const d = document.querySelector("#gradient-indicator");
573
624
  d && (d.remove(), console.log("Gradient indicator removed from the DOM"));
574
625
  }
575
- class V {
626
+ function V() {
627
+ return typeof window < "u" && typeof document < "u";
628
+ }
629
+ class G {
576
630
  constructor() {
577
631
  this.state = "idle", this.subscribers = [];
578
632
  }
@@ -580,7 +634,7 @@ class V {
580
634
  return this.state;
581
635
  }
582
636
  setState(t) {
583
- this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? j() : U();
637
+ this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? $() : j();
584
638
  }
585
639
  // eslint-disable-next-line no-unused-vars
586
640
  subscribe(t) {
@@ -590,9 +644,9 @@ class V {
590
644
  this.subscribers.forEach((t) => t(this.state));
591
645
  }
592
646
  }
593
- class G {
647
+ class Y {
594
648
  constructor(t) {
595
- this.container = null, this.chatWindow = null, this.messagesContainer = null, this.input = null, this.isOpen = !1, this.loadingEl = null, this.config = t, this.init();
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();
596
650
  }
597
651
  init() {
598
652
  var e, i;
@@ -606,10 +660,10 @@ class G {
606
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();
607
661
  }
608
662
  renderFloatingButton() {
609
- var s, n, a, r, c, f;
663
+ var s, n, o, r, c, f;
610
664
  const t = document.createElement("button");
611
665
  t.innerHTML = ((s = this.config.floatingButton) == null ? void 0 : s.customHtml) || "🎙️";
612
- const e = ((a = (n = this.config.floatingButton) == null ? void 0 : n.position) == null ? void 0 : a.bottom) || "20px", i = ((c = (r = this.config.floatingButton) == null ? void 0 : r.position) == null ? void 0 : c.right) || "20px";
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";
613
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);
614
668
  }
615
669
  renderChatWindow() {
@@ -623,26 +677,26 @@ class G {
623
677
  const i = document.createElement("button");
624
678
  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";
625
679
  const s = document.createElement("div");
626
- 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) => {
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) => {
627
681
  var r;
628
- if (a.key === "Enter" && ((r = this.input) != null && r.value.trim())) {
682
+ if (o.key === "Enter" && ((r = this.input) != null && r.value.trim())) {
629
683
  const c = this.input.value.trim();
630
684
  this.input.value = "", this.onSubmit && this.onSubmit(c);
631
685
  }
632
686
  }), 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);
633
687
  }
634
688
  registerCallbacks(t, e) {
635
- this.onSubmit = t, this.onClose = e;
689
+ this.active && (this.onSubmit = t, this.onClose = e);
636
690
  }
637
691
  toggle(t, e) {
638
- t && (this.onSubmit = t), e && (this.onClose = e), this.isOpen = !this.isOpen, this.chatWindow && (this.isOpen ? (this.chatWindow.style.display = "flex", requestAnimationFrame(() => {
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(() => {
639
693
  this.chatWindow && (this.chatWindow.style.opacity = "1", this.chatWindow.style.transform = "translateY(0) scale(1)");
640
694
  }), setTimeout(() => {
641
695
  var i;
642
696
  return (i = this.input) == null ? void 0 : i.focus();
643
697
  }, 100)) : (this.chatWindow.style.opacity = "0", this.chatWindow.style.transform = "translateY(20px) scale(0.95)", setTimeout(() => {
644
698
  this.chatWindow && !this.isOpen && (this.chatWindow.style.display = "none");
645
- }, 200), this.onClose && this.onClose()));
699
+ }, 200), this.onClose && this.onClose())));
646
700
  }
647
701
  addMessage(t, e) {
648
702
  if (!this.messagesContainer) return;
@@ -660,11 +714,11 @@ class G {
660
714
  this.onSubmit && this.onSubmit({ commandId: i.commandId });
661
715
  return;
662
716
  }
663
- const a = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
664
- this.onSubmit && this.onSubmit(a);
717
+ const o = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
718
+ this.onSubmit && this.onSubmit(o);
665
719
  };
666
- s.onclick = n, s.onkeydown = (a) => {
667
- (a.key === "Enter" || a.key === " ") && (a.preventDefault(), n());
720
+ s.onclick = n, s.onkeydown = (o) => {
721
+ (o.key === "Enter" || o.key === " ") && (o.preventDefault(), n());
668
722
  }, e.appendChild(s);
669
723
  }), this.messagesContainer.appendChild(e), this.scrollToBottom();
670
724
  }
@@ -673,157 +727,157 @@ class G {
673
727
  this.addMessage(t, "system");
674
728
  const s = document.createElement("form");
675
729
  s.className = "foisit-form";
676
- const n = [], a = (o, p) => {
677
- const b = document.createElement("div");
678
- return b.className = "foisit-form-label", b.innerHTML = o + (p ? ' <span class="foisit-req-star">*</span>' : ""), b;
730
+ const n = [], o = (a, p) => {
731
+ const g = document.createElement("div");
732
+ return g.className = "foisit-form-label", g.innerHTML = a + (p ? ' <span class="foisit-req-star">*</span>' : ""), g;
679
733
  }, r = () => {
680
- const o = document.createElement("div");
681
- return o.className = "foisit-form-error", o.style.display = "none", o;
734
+ const a = document.createElement("div");
735
+ return a.className = "foisit-form-error", a.style.display = "none", a;
682
736
  };
683
- (e ?? []).forEach((o) => {
737
+ (e ?? []).forEach((a) => {
684
738
  const p = document.createElement("div");
685
739
  p.className = "foisit-form-group";
686
- const b = o.description || o.name;
687
- p.appendChild(a(b, o.required));
688
- let h;
689
- if (o.type === "select") {
740
+ const g = a.description || a.name;
741
+ p.appendChild(o(g, a.required));
742
+ let m;
743
+ if (a.type === "select") {
690
744
  const l = document.createElement("select");
691
745
  l.className = "foisit-form-input";
692
- const y = document.createElement("option");
693
- y.value = "", y.textContent = "Select...", l.appendChild(y);
694
- const x = (m) => {
695
- (m ?? []).forEach((w) => {
696
- const g = document.createElement("option");
697
- g.value = String(w.value ?? w.label ?? ""), g.textContent = String(w.label ?? w.value ?? ""), l.appendChild(g);
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);
698
752
  });
699
753
  };
700
- if (Array.isArray(o.options) && o.options.length)
701
- x(o.options);
702
- else if (typeof o.getOptions == "function") {
703
- const m = o.getOptions, w = document.createElement("option");
704
- w.value = "", w.textContent = "Loading...", l.appendChild(w), Promise.resolve().then(() => m()).then((g) => {
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) => {
705
759
  for (; l.options.length > 1; ) l.remove(1);
706
- x(g);
760
+ w(y);
707
761
  }).catch(() => {
708
762
  for (; l.options.length > 1; ) l.remove(1);
709
- const g = document.createElement("option");
710
- g.value = "", g.textContent = "Error loading options", l.appendChild(g);
763
+ const y = document.createElement("option");
764
+ y.value = "", y.textContent = "Error loading options", l.appendChild(y);
711
765
  });
712
766
  }
713
- o.defaultValue != null && (l.value = String(o.defaultValue)), h = l;
714
- } else if (o.type === "file") {
715
- const l = o, y = document.createElement("input");
716
- 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 () => {
717
- const x = Array.from(y.files || []), m = C;
718
- if (m.style.display = "none", m.textContent = "", x.length === 0) return;
719
- const w = l.maxFiles ?? (l.multiple ? 10 : 1);
720
- if (x.length > w) {
721
- m.textContent = `Please select at most ${w} file(s).`, m.style.display = "block";
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";
722
776
  return;
723
777
  }
724
- const g = l.maxSizeBytes ?? 1 / 0, u = x.reduce((v, E) => v + E.size, 0);
725
- if (x.some((v) => v.size > g)) {
726
- m.textContent = `One or more files exceed the maximum size of ${Math.round(g / 1024)} KB.`, m.style.display = "block";
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";
727
781
  return;
728
782
  }
729
783
  const S = l.maxTotalBytes ?? 1 / 0;
730
784
  if (u > S) {
731
- m.textContent = `Total selected files exceed the maximum of ${Math.round(S / 1024)} KB.`, m.style.display = "block";
785
+ h.textContent = `Total selected files exceed the maximum of ${Math.round(S / 1024)} KB.`, h.style.display = "block";
732
786
  return;
733
787
  }
734
788
  if (l.accept && Array.isArray(l.accept)) {
735
789
  const v = l.accept;
736
- if (!x.every((A) => A.type ? v.some((L) => L.startsWith(".") ? A.name.toLowerCase().endsWith(L.toLowerCase()) : A.type === L || A.type.startsWith(L.split("/")[0] + "/")) : !0)) {
737
- m.textContent = "One or more files have an unsupported type.", m.style.display = "block";
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)) {
791
+ h.textContent = "One or more files have an unsupported type.", h.style.display = "block";
738
792
  return;
739
793
  }
740
794
  }
741
- }), h = y;
795
+ }), m = b;
742
796
  } else {
743
797
  const l = document.createElement("input");
744
- l.className = "foisit-form-input", o.type === "string" && (l.placeholder = o.placeholder || "Type here..."), o.type === "number" ? (l.type = "number", typeof o.min == "number" && (l.min = String(o.min)), typeof o.max == "number" && (l.max = String(o.max)), typeof o.step == "number" && (l.step = String(o.step)), o.defaultValue != null && (l.value = String(o.defaultValue))) : o.type === "date" ? (l.type = "date", typeof o.min == "string" && (l.min = o.min), typeof o.max == "string" && (l.max = o.max), o.defaultValue != null && (l.value = String(o.defaultValue))) : (l.type = "text", o.defaultValue != null && (l.value = String(o.defaultValue))), h = l;
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;
745
799
  }
746
800
  const C = r();
747
- p.appendChild(h), p.appendChild(C), n.push({
748
- name: o.name,
749
- type: o.type,
750
- el: h,
751
- required: o.required
801
+ p.appendChild(m), p.appendChild(C), n.push({
802
+ name: a.name,
803
+ type: a.type,
804
+ el: m,
805
+ required: a.required
752
806
  }), s.appendChild(p);
753
807
  });
754
808
  const c = document.createElement("div");
755
809
  c.className = "foisit-form-actions";
756
810
  const f = document.createElement("button");
757
- f.type = "submit", f.textContent = "Submit", f.className = "foisit-option-chip", f.style.fontWeight = "600", c.appendChild(f), s.appendChild(c), s.onsubmit = async (o) => {
758
- o.preventDefault();
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();
759
813
  const p = {};
760
- let b = !1;
761
- s.querySelectorAll(".foisit-form-error").forEach((h) => {
762
- h.style.display = "none", h.textContent = "";
763
- }), s.querySelectorAll(".foisit-form-input").forEach((h) => {
764
- h.classList.remove("foisit-error-border");
814
+ let g = !1;
815
+ s.querySelectorAll(".foisit-form-error").forEach((m) => {
816
+ m.style.display = "none", m.textContent = "";
817
+ }), s.querySelectorAll(".foisit-form-input").forEach((m) => {
818
+ m.classList.remove("foisit-error-border");
765
819
  });
766
- for (const h of n) {
767
- if (h.type === "file") {
768
- const x = h.el.parentElement, m = x == null ? void 0 : x.querySelector(".foisit-form-error"), w = h.el, g = Array.from(w.files || []);
769
- if (h.required && g.length === 0) {
770
- b = !0, w.classList.add("foisit-error-border"), m && (m.textContent = "This file is required", m.style.display = "block");
820
+ for (const m of n) {
821
+ 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");
771
825
  continue;
772
826
  }
773
- if (g.length === 0) continue;
774
- const u = (e ?? []).find((v) => v.name === h.name), S = (u == null ? void 0 : u.delivery) ?? "file";
827
+ if (y.length === 0) continue;
828
+ const u = (e ?? []).find((v) => v.name === m.name), S = (u == null ? void 0 : u.delivery) ?? "file";
775
829
  if (u != null && u.maxWidth || u != null && u.maxHeight)
776
830
  try {
777
- const v = await this.getImageDimensions(g[0]);
831
+ const v = await this.getImageDimensions(y[0]);
778
832
  if (u.maxWidth && v.width > u.maxWidth) {
779
- b = !0, m && (m.textContent = `Image width must be ≤ ${u.maxWidth}px`, m.style.display = "block");
833
+ g = !0, h && (h.textContent = `Image width must be ≤ ${u.maxWidth}px`, h.style.display = "block");
780
834
  continue;
781
835
  }
782
836
  if (u.maxHeight && v.height > u.maxHeight) {
783
- b = !0, m && (m.textContent = `Image height must be ≤ ${u.maxHeight}px`, m.style.display = "block");
837
+ g = !0, h && (h.textContent = `Image height must be ≤ ${u.maxHeight}px`, h.style.display = "block");
784
838
  continue;
785
839
  }
786
840
  } catch {
787
841
  }
788
842
  if (u != null && u.maxDurationSec)
789
843
  try {
790
- const v = await this.getMediaDuration(g[0]);
844
+ const v = await this.getMediaDuration(y[0]);
791
845
  if (v && v > u.maxDurationSec) {
792
- b = !0, m && (m.textContent = `Media duration must be ≤ ${u.maxDurationSec}s`, m.style.display = "block");
846
+ g = !0, h && (h.textContent = `Media duration must be ≤ ${u.maxDurationSec}s`, h.style.display = "block");
793
847
  continue;
794
848
  }
795
849
  } catch {
796
850
  }
797
851
  if (S === "file")
798
- p[h.name] = u != null && u.multiple ? g : g[0];
852
+ p[m.name] = u != null && u.multiple ? y : y[0];
799
853
  else if (S === "base64")
800
854
  try {
801
- const v = await Promise.all(g.map((E) => this.readFileAsDataURL(E)));
802
- p[h.name] = u != null && u.multiple ? v : v[0];
855
+ const v = await Promise.all(y.map((A) => this.readFileAsDataURL(A)));
856
+ p[m.name] = u != null && u.multiple ? v : v[0];
803
857
  } catch {
804
- b = !0, m && (m.textContent = "Failed to encode file(s) to base64.", m.style.display = "block");
858
+ g = !0, h && (h.textContent = "Failed to encode file(s) to base64.", h.style.display = "block");
805
859
  continue;
806
860
  }
807
861
  continue;
808
862
  }
809
- const C = (h.el.value ?? "").toString().trim(), l = h.el.parentElement, y = l == null ? void 0 : l.querySelector(".foisit-form-error");
810
- if (h.required && (C == null || C === "")) {
811
- b = !0, h.el.classList.add("foisit-error-border"), y && (y.textContent = "This field is required", y.style.display = "block");
863
+ const C = (m.el.value ?? "").toString().trim(), l = m.el.parentElement, b = l == null ? void 0 : l.querySelector(".foisit-form-error");
864
+ 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");
812
866
  continue;
813
867
  }
814
868
  if (C !== "")
815
- if (h.type === "number") {
816
- const x = Number(C);
817
- Number.isNaN(x) || (p[h.name] = x);
869
+ if (m.type === "number") {
870
+ const w = Number(C);
871
+ Number.isNaN(w) || (p[m.name] = w);
818
872
  } else
819
- p[h.name] = C;
873
+ p[m.name] = C;
820
874
  }
821
- if (b) {
875
+ if (g) {
822
876
  s.classList.add("foisit-shake"), setTimeout(() => s.classList.remove("foisit-shake"), 400);
823
877
  return;
824
878
  }
825
- f.disabled = !0, f.style.opacity = "0.6", n.forEach((h) => {
826
- h.el.disabled = !0;
879
+ f.disabled = !0, f.style.opacity = "0.6", n.forEach((m) => {
880
+ m.el.disabled = !0;
827
881
  }), i(p);
828
882
  }, this.messagesContainer.appendChild(s), this.scrollToBottom();
829
883
  }
@@ -874,16 +928,16 @@ class G {
874
928
  try {
875
929
  const i = URL.createObjectURL(t), s = t.type.startsWith("audio") ? document.createElement("audio") : document.createElement("video");
876
930
  let n = !1;
877
- const a = setTimeout(() => {
931
+ const o = setTimeout(() => {
878
932
  n || (n = !0, URL.revokeObjectURL(i), e(0));
879
933
  }, 5e3);
880
934
  s.preload = "metadata", s.onloadedmetadata = () => {
881
935
  if (n) return;
882
- n = !0, clearTimeout(a);
936
+ n = !0, clearTimeout(o);
883
937
  const c = s.duration || 0;
884
938
  URL.revokeObjectURL(i), e(c);
885
939
  }, s.onerror = () => {
886
- n || (n = !0, clearTimeout(a), URL.revokeObjectURL(i), e(0));
940
+ n || (n = !0, clearTimeout(o), URL.revokeObjectURL(i), e(0));
887
941
  }, s.src = i;
888
942
  } catch {
889
943
  e(0);
@@ -1217,12 +1271,12 @@ class G {
1217
1271
  `, document.head.appendChild(t);
1218
1272
  }
1219
1273
  }
1220
- class Y {
1274
+ class K {
1221
1275
  constructor(t) {
1222
1276
  this.config = t, this.isActivated = !1, this.lastProcessedInput = "", this.processingLock = !1, this.defaultIntroMessage = "How can I help you?", this.commandHandler = new B({
1223
1277
  enableSmartIntent: this.config.enableSmartIntent !== !1,
1224
1278
  intentEndpoint: this.config.intentEndpoint
1225
- }), this.fallbackHandler = new O(), this.voiceProcessor = new z(), this.textToSpeech = new H(), this.stateManager = new V(), this.gestureHandler = new $(), this.overlayManager = new G({
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({
1226
1280
  floatingButton: this.config.floatingButton,
1227
1281
  inputPlaceholder: this.config.inputPlaceholder
1228
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(
@@ -1239,15 +1293,22 @@ class Y {
1239
1293
  }
1240
1294
  },
1241
1295
  () => console.log("AssistantService: Overlay closed.")
1242
- );
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));
1243
1297
  }
1244
1298
  /** Start listening for activation and commands */
1245
1299
  startListening() {
1246
- console.log("AssistantService: Voice is disabled; startListening() is a no-op.");
1300
+ if (typeof window > "u" || !this.voiceProcessor) {
1301
+ console.log("AssistantService: Voice is disabled or unavailable; startListening() is a no-op.");
1302
+ return;
1303
+ }
1247
1304
  }
1248
1305
  /** Stop listening */
1249
1306
  stopListening() {
1250
- console.log("AssistantService: Voice is disabled; stopListening() is a no-op."), this.isActivated = !1;
1307
+ if (typeof window > "u" || !this.voiceProcessor) {
1308
+ console.log("AssistantService: Voice unavailable; stopListening() is a no-op."), this.isActivated = !1;
1309
+ return;
1310
+ }
1311
+ this.voiceProcessor.stopListening(), this.isActivated = !1;
1251
1312
  }
1252
1313
  /**
1253
1314
  * Reset activation state so the next activation flow can occur.
@@ -1298,7 +1359,11 @@ class Y {
1298
1359
  this.fallbackHandler.handleFallback(t), this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), "system");
1299
1360
  return;
1300
1361
  }
1301
- e.message ? this.overlayManager.addMessage(e.message, "system") : (e.type === "ambiguous" || e.type === "confirm") && e.options && this.overlayManager.addOptions(e.options);
1362
+ if ((e.type === "ambiguous" || e.type === "confirm") && e.options) {
1363
+ e.message && this.overlayManager.addMessage(e.message, "system"), this.overlayManager.addOptions(e.options);
1364
+ return;
1365
+ }
1366
+ e.message && this.overlayManager.addMessage(e.message, "system");
1302
1367
  }
1303
1368
  /**
1304
1369
  * Cleanup resources
@@ -1356,13 +1421,13 @@ class Y {
1356
1421
  if (i && typeof i == "object") {
1357
1422
  const s = i, n = s.label ?? s.commandId ?? "Selection";
1358
1423
  this.overlayManager.addMessage(String(n), "user"), this.overlayManager.showLoading();
1359
- let a;
1424
+ let o;
1360
1425
  try {
1361
- a = await this.commandHandler.executeCommand(s);
1426
+ o = await this.commandHandler.executeCommand(s);
1362
1427
  } finally {
1363
1428
  this.overlayManager.hideLoading();
1364
1429
  }
1365
- this.processResponse(a);
1430
+ this.processResponse(o);
1366
1431
  }
1367
1432
  },
1368
1433
  () => {
@@ -1378,27 +1443,27 @@ const Z = ({ config: d, children: t }) => {
1378
1443
  return P(() => {
1379
1444
  I ? console.warn(
1380
1445
  "Multiple AssistantProvider instances detected. Reusing global AssistantService."
1381
- ) : (console.log("Initializing global AssistantService..."), I = new Y(d));
1382
- const a = I;
1383
- return i(a), n(!0), () => {
1446
+ ) : (console.log("Initializing global AssistantService..."), I = new K(d));
1447
+ const o = I;
1448
+ return i(o), n(!0), () => {
1384
1449
  var r;
1385
- console.log("Cleaning up AssistantService..."), (r = a.destroy) == null || r.call(a), I = null;
1450
+ console.log("Cleaning up AssistantService..."), (r = o.destroy) == null || r.call(o), I = null;
1386
1451
  };
1387
1452
  }, [d]), !s || !e ? /* @__PURE__ */ k("div", { children: "Loading Assistant..." }) : /* @__PURE__ */ k(q.Provider, { value: e, children: t });
1388
- }, K = () => {
1453
+ }, X = () => {
1389
1454
  const d = N(q);
1390
1455
  if (console.log("assistant", d), !d)
1391
1456
  throw new Error("useAssistant must be used within an AssistantProvider");
1392
1457
  return d;
1393
- }, Q = ({
1458
+ }, tt = ({
1394
1459
  label: d = "Activate Assistant",
1395
1460
  onActivate: t
1396
1461
  }) => {
1397
- const e = K();
1462
+ const e = X();
1398
1463
  return /* @__PURE__ */ k("button", { onClick: () => {
1399
1464
  t && t(), e.reactivate();
1400
1465
  }, className: "assistant-activator", children: d });
1401
- }, tt = (d) => {
1466
+ }, et = (d) => {
1402
1467
  const [t, e] = R(d.getState());
1403
1468
  return P(() => {
1404
1469
  const i = (s) => {
@@ -1411,11 +1476,11 @@ const Z = ({ config: d, children: t }) => {
1411
1476
  }, [d]), t;
1412
1477
  };
1413
1478
  export {
1414
- Q as AssistantActivator,
1479
+ tt as AssistantActivator,
1415
1480
  q as AssistantContext,
1416
1481
  Z as AssistantProvider,
1417
- Y as AssistantService,
1482
+ K as AssistantService,
1418
1483
  J as ReactWrapper,
1419
- K as useAssistant,
1420
- tt as useAssistantState
1484
+ X as useAssistant,
1485
+ et as useAssistantState
1421
1486
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foisit/react-wrapper",
3
- "version": "2.4.1",
3
+ "version": "2.4.4",
4
4
  "main": "./index.js",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {
@@ -19,7 +19,20 @@
19
19
  "README.md"
20
20
  ],
21
21
  "sideEffects": false,
22
- "description": "Foisit: Speak, and its done. A voice assistant library for react apps",
22
+ "description": "A powerful AI assistant library for React applications, providing seamless chatbot functionality and intelligent interactions for websites.",
23
+ "keywords": [
24
+ "ai-assistant",
25
+ "chatbot",
26
+ "react",
27
+ "ai",
28
+ "assistant",
29
+ "accessibility",
30
+ "website-integration"
31
+ ],
32
+ "homepage": "https://github.com/boluwatifee4/foisit#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/boluwatifee4/foisit/issues"
35
+ },
23
36
  "repository": {
24
37
  "type": "git",
25
38
  "url": "git+https://github.com/boluwatifee4/foisit.git"