@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.
- package/README.md +22 -0
- package/index.js +3 -3
- package/index.mjs +213 -159
- 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="×",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="×",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
|
|
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
|
|
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 ?? {},
|
|
70
|
-
if (!
|
|
71
|
-
const p = this.sanitizeParamsForCommand(
|
|
72
|
-
return
|
|
73
|
-
message: `Please provide the required details for "${
|
|
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:
|
|
76
|
-
}) :
|
|
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
|
|
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:
|
|
92
|
+
params: o
|
|
93
93
|
}, this.buildConfirmResponse(n);
|
|
94
|
-
const r = await this.safeRunAction(n,
|
|
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:
|
|
101
|
+
const { commandId: o, params: r } = this.pendingConfirmation;
|
|
102
102
|
this.pendingConfirmation = null;
|
|
103
|
-
const c = this.getCommandById(
|
|
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,
|
|
118
|
-
return
|
|
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:
|
|
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(),
|
|
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(
|
|
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),
|
|
142
|
-
if (t.incomplete ||
|
|
143
|
-
this.context = { commandId: this.getCommandIdentifier(e), params:
|
|
144
|
-
|
|
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 ${
|
|
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:
|
|
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:
|
|
159
|
+
params: n
|
|
162
160
|
}, this.buildConfirmResponse(e);
|
|
163
|
-
const
|
|
164
|
-
return this.normalizeResponse(
|
|
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
|
|
181
|
-
if (typeof
|
|
182
|
-
const
|
|
183
|
-
this.isIsoDateString(
|
|
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
|
|
210
|
-
for (const p of
|
|
211
|
-
const
|
|
212
|
-
|
|
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,
|
|
230
|
-
return
|
|
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:
|
|
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),
|
|
255
|
-
if (n &&
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
422
|
-
const r = t.results[
|
|
423
|
-
if (f && !(!r.isFinal && e.interimResults === !1) && !(r.isFinal &&
|
|
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
|
|
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
|
|
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" ?
|
|
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,
|
|
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 = ((
|
|
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 = "×", 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", (
|
|
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 (
|
|
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
|
|
664
|
-
this.onSubmit && this.onSubmit(
|
|
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 = (
|
|
667
|
-
(
|
|
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 = [],
|
|
677
|
-
const
|
|
678
|
-
return
|
|
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
|
|
681
|
-
return
|
|
730
|
+
const a = document.createElement("div");
|
|
731
|
+
return a.className = "foisit-form-error", a.style.display = "none", a;
|
|
682
732
|
};
|
|
683
|
-
(e ?? []).forEach((
|
|
733
|
+
(e ?? []).forEach((a) => {
|
|
684
734
|
const p = document.createElement("div");
|
|
685
735
|
p.className = "foisit-form-group";
|
|
686
|
-
const
|
|
687
|
-
p.appendChild(
|
|
688
|
-
let
|
|
689
|
-
if (
|
|
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
|
|
693
|
-
|
|
694
|
-
const x = (
|
|
695
|
-
(
|
|
696
|
-
const
|
|
697
|
-
|
|
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(
|
|
701
|
-
x(
|
|
702
|
-
else if (typeof
|
|
703
|
-
const
|
|
704
|
-
w.value = "", w.textContent = "Loading...", l.appendChild(w), Promise.resolve().then(() =>
|
|
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(
|
|
756
|
+
x(y);
|
|
707
757
|
}).catch(() => {
|
|
708
758
|
for (; l.options.length > 1; ) l.remove(1);
|
|
709
|
-
const
|
|
710
|
-
|
|
759
|
+
const y = document.createElement("option");
|
|
760
|
+
y.value = "", y.textContent = "Error loading options", l.appendChild(y);
|
|
711
761
|
});
|
|
712
762
|
}
|
|
713
|
-
|
|
714
|
-
} else if (
|
|
715
|
-
const l =
|
|
716
|
-
|
|
717
|
-
const x = Array.from(
|
|
718
|
-
if (
|
|
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
|
-
|
|
771
|
+
h.textContent = `Please select at most ${w} file(s).`, h.style.display = "block";
|
|
722
772
|
return;
|
|
723
773
|
}
|
|
724
|
-
const
|
|
725
|
-
if (x.some((v) => v.size >
|
|
726
|
-
|
|
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
|
-
|
|
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((
|
|
737
|
-
|
|
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
|
-
}),
|
|
791
|
+
}), m = b;
|
|
742
792
|
} else {
|
|
743
793
|
const l = document.createElement("input");
|
|
744
|
-
l.className = "foisit-form-input",
|
|
794
|
+
l.className = "foisit-form-input", a.type === "string" && (l.placeholder = a.placeholder || "Type here..."), a.type === "number" ? (l.type = "number", typeof a.min == "number" && (l.min = String(a.min)), typeof a.max == "number" && (l.max = String(a.max)), typeof a.step == "number" && (l.step = String(a.step)), a.defaultValue != null && (l.value = String(a.defaultValue))) : a.type === "date" ? (l.type = "date", typeof a.min == "string" && (l.min = a.min), typeof a.max == "string" && (l.max = a.max), a.defaultValue != null && (l.value = String(a.defaultValue))) : (l.type = "text", a.defaultValue != null && (l.value = String(a.defaultValue))), m = l;
|
|
745
795
|
}
|
|
746
796
|
const C = r();
|
|
747
|
-
p.appendChild(
|
|
748
|
-
name:
|
|
749
|
-
type:
|
|
750
|
-
el:
|
|
751
|
-
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 (
|
|
758
|
-
|
|
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
|
|
761
|
-
s.querySelectorAll(".foisit-form-error").forEach((
|
|
762
|
-
|
|
763
|
-
}), s.querySelectorAll(".foisit-form-input").forEach((
|
|
764
|
-
|
|
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
|
|
767
|
-
if (
|
|
768
|
-
const x =
|
|
769
|
-
if (
|
|
770
|
-
|
|
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 (
|
|
774
|
-
const u = (e ?? []).find((v) => v.name ===
|
|
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(
|
|
827
|
+
const v = await this.getImageDimensions(y[0]);
|
|
778
828
|
if (u.maxWidth && v.width > u.maxWidth) {
|
|
779
|
-
|
|
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
|
-
|
|
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(
|
|
840
|
+
const v = await this.getMediaDuration(y[0]);
|
|
791
841
|
if (v && v > u.maxDurationSec) {
|
|
792
|
-
|
|
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[
|
|
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(
|
|
802
|
-
p[
|
|
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
|
-
|
|
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 = (
|
|
810
|
-
if (
|
|
811
|
-
|
|
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 (
|
|
865
|
+
if (m.type === "number") {
|
|
816
866
|
const x = Number(C);
|
|
817
|
-
Number.isNaN(x) || (p[
|
|
867
|
+
Number.isNaN(x) || (p[m.name] = x);
|
|
818
868
|
} else
|
|
819
|
-
p[
|
|
869
|
+
p[m.name] = C;
|
|
820
870
|
}
|
|
821
|
-
if (
|
|
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((
|
|
826
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
1413
|
+
let o;
|
|
1360
1414
|
try {
|
|
1361
|
-
|
|
1415
|
+
o = await this.commandHandler.executeCommand(s);
|
|
1362
1416
|
} finally {
|
|
1363
1417
|
this.overlayManager.hideLoading();
|
|
1364
1418
|
}
|
|
1365
|
-
this.processResponse(
|
|
1419
|
+
this.processResponse(o);
|
|
1366
1420
|
}
|
|
1367
1421
|
},
|
|
1368
1422
|
() => {
|
|
@@ -1371,26 +1425,26 @@ class Y {
|
|
|
1371
1425
|
);
|
|
1372
1426
|
}
|
|
1373
1427
|
}
|
|
1374
|
-
const
|
|
1428
|
+
const H = F(null);
|
|
1375
1429
|
let I = null;
|
|
1376
|
-
const
|
|
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
|
|
1383
|
-
return i(
|
|
1436
|
+
const o = I;
|
|
1437
|
+
return i(o), n(!0), () => {
|
|
1384
1438
|
var r;
|
|
1385
|
-
console.log("Cleaning up AssistantService..."), (r =
|
|
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(
|
|
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(
|
|
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
|
-
},
|
|
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
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1468
|
+
Z as AssistantActivator,
|
|
1469
|
+
H as AssistantContext,
|
|
1470
|
+
J as AssistantProvider,
|
|
1417
1471
|
Y as AssistantService,
|
|
1418
|
-
|
|
1472
|
+
Q as ReactWrapper,
|
|
1419
1473
|
K as useAssistant,
|
|
1420
1474
|
tt as useAssistantState
|
|
1421
1475
|
};
|