@foisit/react-wrapper 2.4.1 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +22 -0
  2. package/index.js +3 -3
  3. package/index.mjs +213 -159
  4. package/package.json +1 -1
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
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 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 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 q().speak(this.fallbackMessage)}getFallbackMessage(){return this.fallbackMessage}}const P=()=>{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");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 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"))}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():$()}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,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.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 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 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 z,this.voiceProcessor=new O,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}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 Y=({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 H(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},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]=C.useState(d.getState());return C.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=W;exports.useAssistant=F;exports.useAssistantState=X;
package/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as k } from "react/jsx-runtime";
2
2
  import { createContext as F, useState as R, useEffect as P, useContext as N } from "react";
3
3
  const W = {};
4
- function J() {
4
+ function Q() {
5
5
  return /* @__PURE__ */ k("div", { className: W.container, children: /* @__PURE__ */ k("h1", { children: "Welcome to ReactWrapper!" }) });
6
6
  }
7
7
  class M {
@@ -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,25 +82,25 @@ 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
106
  return ["no", "n", "cancel", "stop"].includes(n) ? (this.pendingConfirmation = null, { message: "✅ Cancelled.", type: "success" }) : {
@@ -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;
185
225
  }
226
+ if (s.type === "file") {
227
+ const o = i[s.name], r = o && typeof o == "object" && typeof o.name == "string" && typeof o.size == "number", c = typeof o == "string" && /^data:[^;]+;base64,/.test(o);
228
+ (s.delivery ?? "file") === "base64" ? !c && !r && delete i[s.name] : r || delete i[s.name];
229
+ }
230
+ }
186
231
  return i;
187
232
  }
188
233
  isIsoDateString(t) {
@@ -190,6 +235,11 @@ 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
245
  message: `⚠️ Are you sure you want to run "${t.command}"?`,
@@ -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
  })
@@ -295,7 +345,7 @@ class B {
295
345
  return Array.from(this.commands.keys());
296
346
  }
297
347
  }
298
- class H {
348
+ class q {
299
349
  constructor() {
300
350
  this.synth = window.speechSynthesis;
301
351
  }
@@ -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
  }
@@ -325,7 +375,7 @@ class O {
325
375
  this.fallbackMessage = t;
326
376
  }
327
377
  handleFallback(t) {
328
- t && console.log(`Fallback triggered for: "${t}"`), console.log(this.fallbackMessage), new H().speak(this.fallbackMessage);
378
+ t && console.log(`Fallback triggered for: "${t}"`), console.log(this.fallbackMessage), new q().speak(this.fallbackMessage);
329
379
  }
330
380
  getFallbackMessage() {
331
381
  return this.fallbackMessage;
@@ -335,7 +385,7 @@ const T = () => {
335
385
  const d = window;
336
386
  return d.SpeechRecognition ?? d.webkitSpeechRecognition ?? null;
337
387
  };
338
- class z {
388
+ class O {
339
389
  constructor(t = "en-US", e = {}) {
340
390
  this.recognition = null, this.isListening = !1, this.engineActive = !1, this.intentionallyStopped = !1, this.restartAllowed = !0, this.lastStart = 0, this.backoffMs = 250, this.destroyed = !1, this.resultCallback = null, this.ttsSpeaking = !1, this.debugEnabled = !0, this.restartTimer = null, this.prewarmed = !1, this.hadResultThisSession = !1, this.onTTSStart = () => {
341
391
  var s;
@@ -418,9 +468,9 @@ class z {
418
468
  var s, n;
419
469
  if (!this.resultCallback) return;
420
470
  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))
471
+ for (let o = t.resultIndex; o < t.results.length; o++) {
472
+ const r = t.results[o], c = r && r[0], f = ((n = (s = c == null ? void 0 : c.transcript) == null ? void 0 : s.trim) == null ? void 0 : n.call(s)) || "", a = (c == null ? void 0 : c.confidence) ?? 0;
473
+ if (f && !(!r.isFinal && e.interimResults === !1) && !(r.isFinal && a < i))
424
474
  try {
425
475
  this.hadResultThisSession = !0, this.resultCallback(f, !!r.isFinal);
426
476
  } catch {
@@ -501,7 +551,7 @@ class z {
501
551
  }
502
552
  }
503
553
  }
504
- class $ {
554
+ class U {
505
555
  constructor() {
506
556
  this.lastTap = 0;
507
557
  }
@@ -562,13 +612,13 @@ function D() {
562
612
  }
563
613
  `, document.head.appendChild(t), console.log("Gradient styles injected");
564
614
  }
565
- function j() {
615
+ function $() {
566
616
  if (document.querySelector("#gradient-indicator"))
567
617
  return;
568
618
  const d = document.createElement("div");
569
619
  d.id = "gradient-indicator", D(), d.classList.add("gradient-indicator"), document.body.appendChild(d), console.log("Gradient indicator added to the DOM");
570
620
  }
571
- function U() {
621
+ function j() {
572
622
  const d = document.querySelector("#gradient-indicator");
573
623
  d && (d.remove(), console.log("Gradient indicator removed from the DOM"));
574
624
  }
@@ -580,7 +630,7 @@ class V {
580
630
  return this.state;
581
631
  }
582
632
  setState(t) {
583
- this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? j() : U();
633
+ this.state = t, this.notifySubscribers(), console.log("State updated:", t), t === "listening" ? $() : j();
584
634
  }
585
635
  // eslint-disable-next-line no-unused-vars
586
636
  subscribe(t) {
@@ -606,10 +656,10 @@ class G {
606
656
  this.container = document.createElement("div"), this.container.id = "foisit-overlay-container", this.container.className = "foisit-overlay-container", document.body.appendChild(this.container), ((i = this.config.floatingButton) == null ? void 0 : i.visible) !== !1 && this.renderFloatingButton(), this.renderChatWindow();
607
657
  }
608
658
  renderFloatingButton() {
609
- var s, n, a, r, c, f;
659
+ var s, n, o, r, c, f;
610
660
  const t = document.createElement("button");
611
661
  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";
662
+ const e = ((o = (n = this.config.floatingButton) == null ? void 0 : n.position) == null ? void 0 : o.bottom) || "20px", i = ((c = (r = this.config.floatingButton) == null ? void 0 : r.position) == null ? void 0 : c.right) || "20px";
613
663
  t.className = "foisit-floating-btn", t.style.bottom = e, t.style.right = i, t.onclick = () => this.toggle(), t.onmouseenter = () => t.style.transform = "scale(1.05)", t.onmouseleave = () => t.style.transform = "scale(1)", (f = this.container) == null || f.appendChild(t);
614
664
  }
615
665
  renderChatWindow() {
@@ -623,9 +673,9 @@ class G {
623
673
  const i = document.createElement("button");
624
674
  i.type = "button", i.className = "foisit-close", i.setAttribute("aria-label", "Close"), i.innerHTML = "&times;", i.addEventListener("click", () => this.toggle()), t.appendChild(e), t.appendChild(i), this.messagesContainer = document.createElement("div"), this.messagesContainer.className = "foisit-messages";
625
675
  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) => {
676
+ s.className = "foisit-input-area", this.input = document.createElement("input"), this.input.placeholder = this.config.inputPlaceholder || "Type a command...", this.input.className = "foisit-input", this.input.addEventListener("keydown", (o) => {
627
677
  var r;
628
- if (a.key === "Enter" && ((r = this.input) != null && r.value.trim())) {
678
+ if (o.key === "Enter" && ((r = this.input) != null && r.value.trim())) {
629
679
  const c = this.input.value.trim();
630
680
  this.input.value = "", this.onSubmit && this.onSubmit(c);
631
681
  }
@@ -660,11 +710,11 @@ class G {
660
710
  this.onSubmit && this.onSubmit({ commandId: i.commandId });
661
711
  return;
662
712
  }
663
- const a = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
664
- this.onSubmit && this.onSubmit(a);
713
+ const o = i && typeof i.value == "string" && i.value.trim() ? i.value : i.label;
714
+ this.onSubmit && this.onSubmit(o);
665
715
  };
666
- s.onclick = n, s.onkeydown = (a) => {
667
- (a.key === "Enter" || a.key === " ") && (a.preventDefault(), n());
716
+ s.onclick = n, s.onkeydown = (o) => {
717
+ (o.key === "Enter" || o.key === " ") && (o.preventDefault(), n());
668
718
  }, e.appendChild(s);
669
719
  }), this.messagesContainer.appendChild(e), this.scrollToBottom();
670
720
  }
@@ -673,157 +723,157 @@ class G {
673
723
  this.addMessage(t, "system");
674
724
  const s = document.createElement("form");
675
725
  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;
726
+ const n = [], o = (a, p) => {
727
+ const g = document.createElement("div");
728
+ return g.className = "foisit-form-label", g.innerHTML = a + (p ? ' <span class="foisit-req-star">*</span>' : ""), g;
679
729
  }, r = () => {
680
- const o = document.createElement("div");
681
- return o.className = "foisit-form-error", o.style.display = "none", o;
730
+ const a = document.createElement("div");
731
+ return a.className = "foisit-form-error", a.style.display = "none", a;
682
732
  };
683
- (e ?? []).forEach((o) => {
733
+ (e ?? []).forEach((a) => {
684
734
  const p = document.createElement("div");
685
735
  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") {
736
+ const g = a.description || a.name;
737
+ p.appendChild(o(g, a.required));
738
+ let m;
739
+ if (a.type === "select") {
690
740
  const l = document.createElement("select");
691
741
  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);
742
+ const b = document.createElement("option");
743
+ b.value = "", b.textContent = "Select...", l.appendChild(b);
744
+ const x = (h) => {
745
+ (h ?? []).forEach((w) => {
746
+ const y = document.createElement("option");
747
+ y.value = String(w.value ?? w.label ?? ""), y.textContent = String(w.label ?? w.value ?? ""), l.appendChild(y);
698
748
  });
699
749
  };
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) => {
750
+ if (Array.isArray(a.options) && a.options.length)
751
+ x(a.options);
752
+ else if (typeof a.getOptions == "function") {
753
+ const h = a.getOptions, w = document.createElement("option");
754
+ w.value = "", w.textContent = "Loading...", l.appendChild(w), Promise.resolve().then(() => h()).then((y) => {
705
755
  for (; l.options.length > 1; ) l.remove(1);
706
- x(g);
756
+ x(y);
707
757
  }).catch(() => {
708
758
  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);
759
+ const y = document.createElement("option");
760
+ y.value = "", y.textContent = "Error loading options", l.appendChild(y);
711
761
  });
712
762
  }
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;
763
+ a.defaultValue != null && (l.value = String(a.defaultValue)), m = l;
764
+ } else if (a.type === "file") {
765
+ const l = a, b = document.createElement("input");
766
+ b.className = "foisit-form-input", b.type = "file", l.accept && Array.isArray(l.accept) && (b.accept = l.accept.join(",")), l.multiple && (b.multiple = !0), l.capture && (l.capture === !0 ? b.setAttribute("capture", "") : b.setAttribute("capture", String(l.capture))), b.addEventListener("change", async () => {
767
+ const x = Array.from(b.files || []), h = C;
768
+ if (h.style.display = "none", h.textContent = "", x.length === 0) return;
719
769
  const w = l.maxFiles ?? (l.multiple ? 10 : 1);
720
770
  if (x.length > w) {
721
- m.textContent = `Please select at most ${w} file(s).`, m.style.display = "block";
771
+ h.textContent = `Please select at most ${w} file(s).`, h.style.display = "block";
722
772
  return;
723
773
  }
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";
774
+ const y = l.maxSizeBytes ?? 1 / 0, u = x.reduce((v, A) => v + A.size, 0);
775
+ if (x.some((v) => v.size > y)) {
776
+ h.textContent = `One or more files exceed the maximum size of ${Math.round(y / 1024)} KB.`, h.style.display = "block";
727
777
  return;
728
778
  }
729
779
  const S = l.maxTotalBytes ?? 1 / 0;
730
780
  if (u > S) {
731
- m.textContent = `Total selected files exceed the maximum of ${Math.round(S / 1024)} KB.`, m.style.display = "block";
781
+ h.textContent = `Total selected files exceed the maximum of ${Math.round(S / 1024)} KB.`, h.style.display = "block";
732
782
  return;
733
783
  }
734
784
  if (l.accept && Array.isArray(l.accept)) {
735
785
  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";
786
+ if (!x.every((E) => E.type ? v.some((L) => L.startsWith(".") ? E.name.toLowerCase().endsWith(L.toLowerCase()) : E.type === L || E.type.startsWith(L.split("/")[0] + "/")) : !0)) {
787
+ h.textContent = "One or more files have an unsupported type.", h.style.display = "block";
738
788
  return;
739
789
  }
740
790
  }
741
- }), h = y;
791
+ }), m = b;
742
792
  } else {
743
793
  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;
794
+ l.className = "foisit-form-input", a.type === "string" && (l.placeholder = a.placeholder || "Type here..."), a.type === "number" ? (l.type = "number", typeof a.min == "number" && (l.min = String(a.min)), typeof a.max == "number" && (l.max = String(a.max)), typeof a.step == "number" && (l.step = String(a.step)), a.defaultValue != null && (l.value = String(a.defaultValue))) : a.type === "date" ? (l.type = "date", typeof a.min == "string" && (l.min = a.min), typeof a.max == "string" && (l.max = a.max), a.defaultValue != null && (l.value = String(a.defaultValue))) : (l.type = "text", a.defaultValue != null && (l.value = String(a.defaultValue))), m = l;
745
795
  }
746
796
  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
797
+ p.appendChild(m), p.appendChild(C), n.push({
798
+ name: a.name,
799
+ type: a.type,
800
+ el: m,
801
+ required: a.required
752
802
  }), s.appendChild(p);
753
803
  });
754
804
  const c = document.createElement("div");
755
805
  c.className = "foisit-form-actions";
756
806
  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();
807
+ f.type = "submit", f.textContent = "Submit", f.className = "foisit-option-chip", f.style.fontWeight = "600", c.appendChild(f), s.appendChild(c), s.onsubmit = async (a) => {
808
+ a.preventDefault();
759
809
  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");
810
+ let g = !1;
811
+ s.querySelectorAll(".foisit-form-error").forEach((m) => {
812
+ m.style.display = "none", m.textContent = "";
813
+ }), s.querySelectorAll(".foisit-form-input").forEach((m) => {
814
+ m.classList.remove("foisit-error-border");
765
815
  });
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");
816
+ for (const m of n) {
817
+ if (m.type === "file") {
818
+ const x = m.el.parentElement, h = x == null ? void 0 : x.querySelector(".foisit-form-error"), w = m.el, y = Array.from(w.files || []);
819
+ if (m.required && y.length === 0) {
820
+ g = !0, w.classList.add("foisit-error-border"), h && (h.textContent = "This file is required", h.style.display = "block");
771
821
  continue;
772
822
  }
773
- if (g.length === 0) continue;
774
- const u = (e ?? []).find((v) => v.name === h.name), S = (u == null ? void 0 : u.delivery) ?? "file";
823
+ if (y.length === 0) continue;
824
+ const u = (e ?? []).find((v) => v.name === m.name), S = (u == null ? void 0 : u.delivery) ?? "file";
775
825
  if (u != null && u.maxWidth || u != null && u.maxHeight)
776
826
  try {
777
- const v = await this.getImageDimensions(g[0]);
827
+ const v = await this.getImageDimensions(y[0]);
778
828
  if (u.maxWidth && v.width > u.maxWidth) {
779
- b = !0, m && (m.textContent = `Image width must be ≤ ${u.maxWidth}px`, m.style.display = "block");
829
+ g = !0, h && (h.textContent = `Image width must be ≤ ${u.maxWidth}px`, h.style.display = "block");
780
830
  continue;
781
831
  }
782
832
  if (u.maxHeight && v.height > u.maxHeight) {
783
- b = !0, m && (m.textContent = `Image height must be ≤ ${u.maxHeight}px`, m.style.display = "block");
833
+ g = !0, h && (h.textContent = `Image height must be ≤ ${u.maxHeight}px`, h.style.display = "block");
784
834
  continue;
785
835
  }
786
836
  } catch {
787
837
  }
788
838
  if (u != null && u.maxDurationSec)
789
839
  try {
790
- const v = await this.getMediaDuration(g[0]);
840
+ const v = await this.getMediaDuration(y[0]);
791
841
  if (v && v > u.maxDurationSec) {
792
- b = !0, m && (m.textContent = `Media duration must be ≤ ${u.maxDurationSec}s`, m.style.display = "block");
842
+ g = !0, h && (h.textContent = `Media duration must be ≤ ${u.maxDurationSec}s`, h.style.display = "block");
793
843
  continue;
794
844
  }
795
845
  } catch {
796
846
  }
797
847
  if (S === "file")
798
- p[h.name] = u != null && u.multiple ? g : g[0];
848
+ p[m.name] = u != null && u.multiple ? y : y[0];
799
849
  else if (S === "base64")
800
850
  try {
801
- const v = await Promise.all(g.map((E) => this.readFileAsDataURL(E)));
802
- p[h.name] = u != null && u.multiple ? v : v[0];
851
+ const v = await Promise.all(y.map((A) => this.readFileAsDataURL(A)));
852
+ p[m.name] = u != null && u.multiple ? v : v[0];
803
853
  } catch {
804
- b = !0, m && (m.textContent = "Failed to encode file(s) to base64.", m.style.display = "block");
854
+ g = !0, h && (h.textContent = "Failed to encode file(s) to base64.", h.style.display = "block");
805
855
  continue;
806
856
  }
807
857
  continue;
808
858
  }
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");
859
+ const C = (m.el.value ?? "").toString().trim(), l = m.el.parentElement, b = l == null ? void 0 : l.querySelector(".foisit-form-error");
860
+ if (m.required && (C == null || C === "")) {
861
+ g = !0, m.el.classList.add("foisit-error-border"), b && (b.textContent = "This field is required", b.style.display = "block");
812
862
  continue;
813
863
  }
814
864
  if (C !== "")
815
- if (h.type === "number") {
865
+ if (m.type === "number") {
816
866
  const x = Number(C);
817
- Number.isNaN(x) || (p[h.name] = x);
867
+ Number.isNaN(x) || (p[m.name] = x);
818
868
  } else
819
- p[h.name] = C;
869
+ p[m.name] = C;
820
870
  }
821
- if (b) {
871
+ if (g) {
822
872
  s.classList.add("foisit-shake"), setTimeout(() => s.classList.remove("foisit-shake"), 400);
823
873
  return;
824
874
  }
825
- f.disabled = !0, f.style.opacity = "0.6", n.forEach((h) => {
826
- h.el.disabled = !0;
875
+ f.disabled = !0, f.style.opacity = "0.6", n.forEach((m) => {
876
+ m.el.disabled = !0;
827
877
  }), i(p);
828
878
  }, this.messagesContainer.appendChild(s), this.scrollToBottom();
829
879
  }
@@ -874,16 +924,16 @@ class G {
874
924
  try {
875
925
  const i = URL.createObjectURL(t), s = t.type.startsWith("audio") ? document.createElement("audio") : document.createElement("video");
876
926
  let n = !1;
877
- const a = setTimeout(() => {
927
+ const o = setTimeout(() => {
878
928
  n || (n = !0, URL.revokeObjectURL(i), e(0));
879
929
  }, 5e3);
880
930
  s.preload = "metadata", s.onloadedmetadata = () => {
881
931
  if (n) return;
882
- n = !0, clearTimeout(a);
932
+ n = !0, clearTimeout(o);
883
933
  const c = s.duration || 0;
884
934
  URL.revokeObjectURL(i), e(c);
885
935
  }, s.onerror = () => {
886
- n || (n = !0, clearTimeout(a), URL.revokeObjectURL(i), e(0));
936
+ n || (n = !0, clearTimeout(o), URL.revokeObjectURL(i), e(0));
887
937
  }, s.src = i;
888
938
  } catch {
889
939
  e(0);
@@ -1222,7 +1272,7 @@ class Y {
1222
1272
  this.config = t, this.isActivated = !1, this.lastProcessedInput = "", this.processingLock = !1, this.defaultIntroMessage = "How can I help you?", this.commandHandler = new B({
1223
1273
  enableSmartIntent: this.config.enableSmartIntent !== !1,
1224
1274
  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({
1275
+ }), this.fallbackHandler = new z(), this.voiceProcessor = new O(), this.textToSpeech = new q(), this.stateManager = new V(), this.gestureHandler = new U(), this.overlayManager = new G({
1226
1276
  floatingButton: this.config.floatingButton,
1227
1277
  inputPlaceholder: this.config.inputPlaceholder
1228
1278
  }), this.config.commands.forEach((e) => this.commandHandler.addCommand(e)), this.config.fallbackResponse && this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse), this.gestureHandler.setupDoubleTapListener(() => this.toggle()), this.overlayManager.registerCallbacks(
@@ -1298,7 +1348,11 @@ class Y {
1298
1348
  this.fallbackHandler.handleFallback(t), this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), "system");
1299
1349
  return;
1300
1350
  }
1301
- e.message ? this.overlayManager.addMessage(e.message, "system") : (e.type === "ambiguous" || e.type === "confirm") && e.options && this.overlayManager.addOptions(e.options);
1351
+ if ((e.type === "ambiguous" || e.type === "confirm") && e.options) {
1352
+ e.message && this.overlayManager.addMessage(e.message, "system"), this.overlayManager.addOptions(e.options);
1353
+ return;
1354
+ }
1355
+ e.message && this.overlayManager.addMessage(e.message, "system");
1302
1356
  }
1303
1357
  /**
1304
1358
  * Cleanup resources
@@ -1356,13 +1410,13 @@ class Y {
1356
1410
  if (i && typeof i == "object") {
1357
1411
  const s = i, n = s.label ?? s.commandId ?? "Selection";
1358
1412
  this.overlayManager.addMessage(String(n), "user"), this.overlayManager.showLoading();
1359
- let a;
1413
+ let o;
1360
1414
  try {
1361
- a = await this.commandHandler.executeCommand(s);
1415
+ o = await this.commandHandler.executeCommand(s);
1362
1416
  } finally {
1363
1417
  this.overlayManager.hideLoading();
1364
1418
  }
1365
- this.processResponse(a);
1419
+ this.processResponse(o);
1366
1420
  }
1367
1421
  },
1368
1422
  () => {
@@ -1371,26 +1425,26 @@ class Y {
1371
1425
  );
1372
1426
  }
1373
1427
  }
1374
- const q = F(null);
1428
+ const H = F(null);
1375
1429
  let I = null;
1376
- const Z = ({ config: d, children: t }) => {
1430
+ const J = ({ config: d, children: t }) => {
1377
1431
  const [e, i] = R(null), [s, n] = R(!1);
1378
1432
  return P(() => {
1379
1433
  I ? console.warn(
1380
1434
  "Multiple AssistantProvider instances detected. Reusing global AssistantService."
1381
1435
  ) : (console.log("Initializing global AssistantService..."), I = new Y(d));
1382
- const a = I;
1383
- return i(a), n(!0), () => {
1436
+ const o = I;
1437
+ return i(o), n(!0), () => {
1384
1438
  var r;
1385
- console.log("Cleaning up AssistantService..."), (r = a.destroy) == null || r.call(a), I = null;
1439
+ console.log("Cleaning up AssistantService..."), (r = o.destroy) == null || r.call(o), I = null;
1386
1440
  };
1387
- }, [d]), !s || !e ? /* @__PURE__ */ k("div", { children: "Loading Assistant..." }) : /* @__PURE__ */ k(q.Provider, { value: e, children: t });
1441
+ }, [d]), !s || !e ? /* @__PURE__ */ k("div", { children: "Loading Assistant..." }) : /* @__PURE__ */ k(H.Provider, { value: e, children: t });
1388
1442
  }, K = () => {
1389
- const d = N(q);
1443
+ const d = N(H);
1390
1444
  if (console.log("assistant", d), !d)
1391
1445
  throw new Error("useAssistant must be used within an AssistantProvider");
1392
1446
  return d;
1393
- }, Q = ({
1447
+ }, Z = ({
1394
1448
  label: d = "Activate Assistant",
1395
1449
  onActivate: t
1396
1450
  }) => {
@@ -1411,11 +1465,11 @@ const Z = ({ config: d, children: t }) => {
1411
1465
  }, [d]), t;
1412
1466
  };
1413
1467
  export {
1414
- Q as AssistantActivator,
1415
- q as AssistantContext,
1416
- Z as AssistantProvider,
1468
+ Z as AssistantActivator,
1469
+ H as AssistantContext,
1470
+ J as AssistantProvider,
1417
1471
  Y as AssistantService,
1418
- J as ReactWrapper,
1472
+ Q as ReactWrapper,
1419
1473
  K as useAssistant,
1420
1474
  tt as useAssistantState
1421
1475
  };
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.2",
4
4
  "main": "./index.js",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {