@foisit/react-wrapper 2.4.2 → 2.4.5
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 +63 -8
- package/index.js +88 -3
- package/index.mjs +442 -215
- package/lib/services/AssistantService.d.ts +10 -0
- package/package.json +22 -2
package/README.md
CHANGED
|
@@ -27,9 +27,12 @@ Transform your React app into an intelligent, voice-ready platform. Foisit provi
|
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
29
|
|
|
30
|
-
- **Natural Language Understanding** - AI-powered intent matching
|
|
30
|
+
- **Natural Language Understanding** - AI-powered intent matching (proxied securely)
|
|
31
31
|
- **Smart Slot Filling** - Auto-generates forms for missing parameters
|
|
32
32
|
- **Critical Action Protection** - Built-in confirmation dialogs for dangerous operations
|
|
33
|
+
- **Programmatic UI Triggers** - Direct command execution via `runCommand()`
|
|
34
|
+
- **Rich Markdown Rendering** - Enhanced response formatting with headings, code, and links
|
|
35
|
+
- **Advanced File Validations** - Comprehensive client-side file validation with size, type, and dimension checks
|
|
33
36
|
- **Premium UI** - Glassmorphic overlay with dark/light mode support
|
|
34
37
|
- **Zero Backend Required** - Secure proxy architecture keeps API keys server-side
|
|
35
38
|
- **React Native** - Uses Hooks, Context API, and modern React patterns
|
|
@@ -194,9 +197,34 @@ Collect files via the built-in form UI and receive them in your command `action`
|
|
|
194
197
|
}
|
|
195
198
|
```
|
|
196
199
|
|
|
197
|
-
`FileParameter` supports validations
|
|
200
|
+
`FileParameter` supports advanced validations:
|
|
198
201
|
|
|
199
|
-
|
|
202
|
+
- `maxFiles`: Maximum number of files allowed (default: 1 for single, 10 for multiple)
|
|
203
|
+
- `maxSizeBytes`: Maximum size per file in bytes
|
|
204
|
+
- `maxTotalBytes`: Maximum total size for all files combined
|
|
205
|
+
- `maxDurationSec`: Maximum duration for media files (audio/video) in seconds
|
|
206
|
+
- `maxWidth` / `maxHeight`: Maximum dimensions for images in pixels
|
|
207
|
+
- `accept`: Allowed MIME types or file extensions (e.g., `['image/*', '.pdf']`)
|
|
208
|
+
- `multiple`: Allow selecting multiple files
|
|
209
|
+
- `capture`: Hint for mobile devices (`'camera'` or `'microphone'`)
|
|
210
|
+
- `delivery`: How files are delivered to your action (`'file'` or `'base64'`)
|
|
211
|
+
|
|
212
|
+
Files are validated client-side before submission, with clear error messages for violations.
|
|
213
|
+
|
|
214
|
+
### 4. Rich Markdown Rendering
|
|
215
|
+
|
|
216
|
+
Assistant responses support rich markdown formatting for enhanced readability:
|
|
217
|
+
|
|
218
|
+
- **Headings**: `# H1` through `###### H6`
|
|
219
|
+
- **Text Formatting**: `**bold**`, `*italic*`, `~~strikethrough~~`
|
|
220
|
+
- **Code**: Inline `code` and code blocks with syntax highlighting
|
|
221
|
+
- **Links**: `[text](url)` with safe external linking
|
|
222
|
+
- **Lists**: Ordered and unordered lists
|
|
223
|
+
- **Line breaks** and paragraphs
|
|
224
|
+
|
|
225
|
+
Markdown is automatically rendered in AI responses, while user messages remain plain text.
|
|
226
|
+
|
|
227
|
+
### 5. Critical Actions
|
|
200
228
|
|
|
201
229
|
Protect dangerous operations with automatic confirmation dialogs:
|
|
202
230
|
|
|
@@ -212,7 +240,7 @@ Protect dangerous operations with automatic confirmation dialogs:
|
|
|
212
240
|
}
|
|
213
241
|
```
|
|
214
242
|
|
|
215
|
-
###
|
|
243
|
+
### 6. Select Parameters (Static)
|
|
216
244
|
|
|
217
245
|
Provide predefined options:
|
|
218
246
|
|
|
@@ -232,7 +260,7 @@ Provide predefined options:
|
|
|
232
260
|
}
|
|
233
261
|
```
|
|
234
262
|
|
|
235
|
-
###
|
|
263
|
+
### 7. Dynamic Select Parameters
|
|
236
264
|
|
|
237
265
|
Load options from APIs:
|
|
238
266
|
|
|
@@ -350,6 +378,36 @@ const commands = assistant.getCommands();
|
|
|
350
378
|
console.log('Available commands:', commands);
|
|
351
379
|
```
|
|
352
380
|
|
|
381
|
+
##### `runCommand(options)`
|
|
382
|
+
|
|
383
|
+
Programmatically trigger a command execution with optional parameters. This allows you to invoke commands directly from your code without user input.
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
// Basic usage - trigger a command by ID
|
|
387
|
+
assistant.runCommand({ commandId: 'refresh-data' });
|
|
388
|
+
|
|
389
|
+
// With parameters
|
|
390
|
+
assistant.runCommand({
|
|
391
|
+
commandId: 'create-user',
|
|
392
|
+
params: { name: 'John', email: 'john@example.com' },
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Open overlay and show the command invocation
|
|
396
|
+
assistant.runCommand({
|
|
397
|
+
commandId: 'export-report',
|
|
398
|
+
params: { format: 'pdf' },
|
|
399
|
+
openOverlay: true,
|
|
400
|
+
showInvocation: true,
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Options:**
|
|
405
|
+
|
|
406
|
+
- `commandId` (required): The ID of the command to run
|
|
407
|
+
- `params` (optional): Parameters to pass to the command action
|
|
408
|
+
- `openOverlay` (optional): Whether to open the assistant overlay (default: false)
|
|
409
|
+
- `showInvocation` (optional): Whether to display the command invocation in the chat (default: false)
|
|
410
|
+
|
|
353
411
|
---
|
|
354
412
|
|
|
355
413
|
## Configuration Options
|
|
@@ -554,8 +612,6 @@ function AccountManager() {
|
|
|
554
612
|
### Full Type Definitions
|
|
555
613
|
|
|
556
614
|
```tsx
|
|
557
|
-
import { AssistantCommand, InteractiveResponse } from '@foisit/core';
|
|
558
|
-
|
|
559
615
|
// Type-safe command definition
|
|
560
616
|
const myCommand: AssistantCommand = {
|
|
561
617
|
command: 'update settings',
|
|
@@ -668,7 +724,6 @@ test('renders assistant', () => {
|
|
|
668
724
|
|
|
669
725
|
## Related Packages
|
|
670
726
|
|
|
671
|
-
- **[@foisit/core](../core)** - Core engine (auto-installed)
|
|
672
727
|
- **[@foisit/angular-wrapper](../angular-wrapper)** - Angular integration
|
|
673
728
|
- **[@foisit/vue-wrapper](../vue-wrapper)** - Vue integration
|
|
674
729
|
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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=`
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=require("react/jsx-runtime"),S=require("react"),O={};function N(){return E.jsx("div",{className:O.container,children:E.jsx("h1",{children:"Welcome to ReactWrapper!"})})}class H{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(o=>({id:o.id,command:o.command,description:o.description,parameters:o.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 z{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 H);return}this.enableSmartIntent=t.enableSmartIntent??!0,this.enableSmartIntent&&(this.openAIService=new H(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??{},r=this.getCommandById(c);if(!r)return{message:"That command is not available.",type:"error"};const p=this.sanitizeParamsForCommand(r,f),m=(r.parameters??[]).filter(C=>C.required).filter(C=>p[C.name]==null||p[C.name]==="");return m.length>0?(this.context={commandId:this.getCommandIdentifier(r),params:p},{message:`Please provide the required details for "${r.command}".`,type:"form",fields:m}):r.critical?(this.pendingConfirmation={commandId:this.getCommandIdentifier(r),params:p},this.buildConfirmResponse(r)):this.safeRunAction(r,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 o=await this.safeRunAction(n,a);return this.context=null,this.normalizeResponse(o)}const e=t.trim().toLowerCase();if(this.pendingConfirmation){const n=e;if(["yes","y","confirm","ok","okay"].includes(n)){const{commandId:a,params:o}=this.pendingConfirmation;this.pendingConfirmation=null;const c=this.getCommandById(a);return c?this.safeRunAction(c,o):{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;if(n.macro)return this.safeRunAction(n,{});const a=(n.parameters??[]).filter(o=>o.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"};if(e.macro)return this.safeRunAction(e,{});const i=t.params??{},s=this.sanitizeParamsForCommand(e,i),n=e.allowAiParamExtraction===!1?{}:s,o=(e.parameters??[]).filter(f=>f.required).filter(f=>n[f.name]==null||n[f.name]==="");if(t.incomplete||o.length>0){if(this.context={commandId:this.getCommandIdentifier(e),params:n},!(e.collectRequiredViaForm!==!1)&&this.shouldAskSingleQuestion(o)){const p=o.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:o}}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 a=n.trim();if(!a){delete i[s.name];continue}i[s.name]=a}if(s.type==="number"){const a=typeof n=="number"?n:Number(n==null?void 0:n.toString().trim());if(Number.isNaN(a)){delete i[s.name];continue}if(typeof s.min=="number"&&a<s.min){delete i[s.name];continue}if(typeof s.max=="number"&&a>s.max){delete i[s.name];continue}i[s.name]=a}if(s.type==="date"){const a=i[s.name];if(typeof a=="string"){const o=a.trim();this.isIsoDateString(o)?i[s.name]=o:delete i[s.name]}else delete i[s.name]}if(s.type==="select"){const a=typeof n=="string"?n:n==null?void 0:n.toString();if(!a){delete i[s.name];continue}if(Array.isArray(s.options)&&s.options.length>0&&!s.options.some(c=>String(c.value)===String(a))){delete i[s.name];continue}i[s.name]=a}if(s.type==="file"){const a=i[s.name],o=a&&typeof a=="object"&&typeof a.name=="string"&&typeof a.size=="number",c=typeof a=="string"&&/^data:[^;]+;base64,/.test(a);(s.delivery??"file")==="base64"?!c&&!o&&delete i[s.name]:o||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 o of this.commands.values()){let c=0;const f=o.command.toLowerCase();t.includes(f)&&(c+=5);const r=o.keywords??[];for(const p of r){const g=p.toLowerCase().trim();g&&(t===g?c+=4:t.includes(g)&&(c+=3))}c>0&&e.push({cmd:o,score:c})}if(e.length===0)return null;e.sort((o,c)=>c.score-o.score);const i=e[0].score,s=e.filter(o=>o.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(o=>({label:o.cmd.command,value:o.cmd.command,commandId:o.cmd.id}))};const n=s[0].cmd,a=(n.parameters??[]).filter(o=>o.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 o=await i.getOptions();this.selectOptionsCache.set(s,{options:o,ts:a}),i.options=o}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 P{constructor(){this.synth=typeof window<"u"?window.speechSynthesis:null}speak(t,e){if(!this.synth){console.error("SpeechSynthesis API is not supported in this environment.");return}const i=new SpeechSynthesisUtterance(t);e&&(i.pitch=e.pitch||1,i.rate=e.rate||1,i.volume=e.volume||1),typeof window<"u"&&(i.onstart=()=>{window.dispatchEvent(new CustomEvent("foisit:tts-start"))},i.onend=()=>{console.log("Speech finished."),window.dispatchEvent(new CustomEvent("foisit:tts-end"))}),i.onerror=s=>{console.error("Error during speech synthesis:",s.error)},this.synth.speak(i)}stopSpeaking(){this.synth&&this.synth.cancel()}}class W{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 P().speak(this.fallbackMessage)}getFallbackMessage(){return this.fallbackMessage}}const $=()=>{if(typeof window>"u")return null;const d=window;return d.SpeechRecognition??d.webkitSpeechRecognition??null};class B{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=$();if(i){this.recognition=new i,this.recognition.lang=t,this.recognition.interimResults=e.interimResults??!0,this.recognition.continuous=e.continuous??!0,this.recognition.onresult=n=>this.handleResult(n,e),this.recognition.onend=()=>this.handleEnd(),this.recognition.onstart=()=>{this.log("recognition onstart"),this.engineActive=!0,this.hadResultThisSession=!1,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null),this.backoffMs=250,this.isListening&&!this.ttsSpeaking&&this.emitStatus("listening")};const s=this.recognition;s.onaudiostart=()=>this.log("onaudiostart"),s.onsoundstart=()=>this.log("onsoundstart"),s.onspeechstart=()=>this.log("onspeechstart"),s.onspeechend=()=>this.log("onspeechend"),s.onsoundend=()=>this.log("onsoundend"),s.onaudioend=()=>this.log("onaudioend"),this.recognition.onerror=n=>this.handleError(n)}else this.recognition=null,this.emitStatus("unsupported");typeof window<"u"?(window.addEventListener("foisit:tts-start",this.onTTSStart),window.addEventListener("foisit:tts-end",this.onTTSEnd),this.visibilityHandler=()=>{var s;if(typeof document<"u"&&document.hidden){try{(s=this.recognition)==null||s.stop()}catch{}this.emitStatus(this.ttsSpeaking?"speaking":"idle")}else this.isListening&&!this.ttsSpeaking&&this.safeRestart()},typeof document<"u"&&document.addEventListener("visibilitychange",this.visibilityHandler)):this.visibilityHandler=void 0}log(t){this.debugEnabled&&t&&console.log("[VoiceProcessor]",t)}warn(t){this.debugEnabled&&t&&console.warn("[VoiceProcessor]",t)}error(t){this.debugEnabled&&t&&console.error("[VoiceProcessor]",t)}isSupported(){return $()!==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 o=t.results[a],c=o&&o[0],f=((n=(s=c==null?void 0:c.transcript)==null?void 0:s.trim)==null?void 0:n.call(s))||"",r=(c==null?void 0:c.confidence)??0;if(f&&!(!o.isFinal&&e.interimResults===!1)&&!(o.isFinal&&r<i))try{this.hadResultThisSession=!0,this.resultCallback(f,!!o.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 R{constructor(){this.lastTap=0,this.tapCount=0}setupTripleTapListener(t){this.destroy(),this.clickListener=()=>{this.tapCount++,this.tapCount===1?this.tapTimeout=window.setTimeout(()=>{this.tapCount=0},500):this.tapCount===3&&(clearTimeout(this.tapTimeout),this.tapCount=0,t())},document.addEventListener("click",this.clickListener),this.touchEndListener=()=>{const e=new Date().getTime(),i=e-this.lastTap;i<500&&i>0?(this.tapCount++,this.tapCount===3&&(this.tapCount=0,t())):this.tapCount=1,this.lastTap=e},document.addEventListener("touchend",this.touchEndListener)}destroy(){this.dblClickListener&&document.removeEventListener("dblclick",this.dblClickListener),this.touchEndListener&&document.removeEventListener("touchend",this.touchEndListener),this.clickListener&&document.removeEventListener("click",this.clickListener),this.tapTimeout&&clearTimeout(this.tapTimeout),this.dblClickListener=void 0,this.touchEndListener=void 0,this.clickListener=void 0,this.tapTimeout=void 0,this.tapCount=0}}function j(){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",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=`
|
|
34
|
+
`,document.head.appendChild(t),console.log("Gradient styles injected")}function U(){if(document.querySelector("#gradient-indicator"))return;const d=document.createElement("div");d.id="gradient-indicator",j(),d.classList.add("gradient-indicator"),document.body.appendChild(d),console.log("Gradient indicator added to the DOM")}function D(){const d=document.querySelector("#gradient-indicator");d&&(d.remove(),console.log("Gradient indicator removed from the DOM"))}function V(){return typeof window<"u"&&typeof document<"u"}class _{constructor(){this.state="idle",this.subscribers=[]}getState(){return this.state}setState(t){this.state=t,this.notifySubscribers(),console.log("State updated:",t),t==="listening"?U():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.commandHandlers=new Map,this.active=V(),this.handleClickOutside=e=>{const i=e.target;this.chatWindow&&this.chatWindow.contains(i)||i.closest(".foisit-floating-btn")||this.toggle()},this.config=t,this.active&&this.init()}registerCommandHandler(t,e){!t||typeof e!="function"||this.commandHandlers.set(t,e)}hasCommandHandler(t){return this.commandHandlers.has(t)}setExternalCommandExecutor(t){this.externalCommandExecutor=t}unregisterCommandHandler(t){t&&this.commandHandlers.delete(t)}async runCommand(t){if(!t||!t.commandId)throw new Error("runCommand requires a commandId");const{commandId:e,params:i,openOverlay:s=!0,showInvocation:n=!0}=t;s&&!this.isOpen&&this.toggle();const a=this.commandHandlers.get(e);if(a&&n&&this.messagesContainer&&this.addMessage(`Command: ${e}`,"user"),a)try{this.showLoading();const o=await a(i);if(this.hideLoading(),typeof o=="string")this.addMessage(o,"system");else if(o&&typeof o=="object")try{this.addMessage(JSON.stringify(o,null,2),"system")}catch{this.addMessage(String(o),"system")}else o==null||this.addMessage(String(o),"system");return o}catch(o){throw this.hideLoading(),this.addMessage(`Command "${e}" failed: ${String(o)}`,"system"),o}if(this.externalCommandExecutor)try{this.showLoading();const o=await this.externalCommandExecutor({commandId:e,params:i});return this.hideLoading(),o}catch(o){throw this.hideLoading(),this.addMessage(`Command "${e}" failed: ${String(o)}`,"system"),o}this.addMessage(`No handler registered for command "${e}".`,"system")}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(),this.config.enableGestureActivation&&(this.gestureHandler=new R,this.gestureHandler.setupTripleTapListener(()=>this.toggle()));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(),this.config.enableGestureActivation&&(this.gestureHandler=new R,this.gestureHandler.setupTripleTapListener(()=>this.toggle()))}renderFloatingButton(){var s,n,a,o,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=(o=this.config.floatingButton)==null?void 0:o.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 o;if(a.key==="Enter"&&((o=this.input)!=null&&o.value.trim())){const c=this.input.value.trim();this.input.value="",this.onSubmit&&this.onSubmit(c)}}),s.appendChild(this.input),this.chatWindow.appendChild(t),this.chatWindow.appendChild(this.messagesContainer),this.chatWindow.appendChild(s),(n=this.container)==null||n.appendChild(this.chatWindow)}registerCallbacks(t,e){this.active&&(this.onSubmit=t,this.onClose=e)}toggle(t,e){this.active&&(t&&(this.onSubmit=t),e&&(this.onClose=e),this.isOpen=!this.isOpen,this.chatWindow&&(this.isOpen?(this.container&&(this.container.style.pointerEvents="auto"),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.addClickOutsideListener()):(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(),this.removeClickOutsideListener(),this.container&&(this.container.style.pointerEvents="none"))))}addClickOutsideListener(){this.container&&(this.removeClickOutsideListener(),this.container.addEventListener("click",this.handleClickOutside))}removeClickOutsideListener(){this.container&&this.container.removeEventListener("click",this.handleClickOutside)}addMessage(t,e){if(!this.messagesContainer)return;const i=document.createElement("div");e==="system"?i.innerHTML=this.renderMarkdown(t):i.textContent=t,i.className=e==="user"?"foisit-bubble user":"foisit-bubble system";const s=(t||"").length||0,n=Math.max(120,700-Math.min(600,Math.floor(s*6)));i.style.opacity="0",i.style.transform="translateY(8px)",i.style.transition="none",this.messagesContainer.appendChild(i),this.animateMessageEntrance(i,n),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=(r,p)=>{const g=document.createElement("div");return g.className="foisit-form-label",g.innerHTML=r+(p?' <span class="foisit-req-star">*</span>':""),g},o=()=>{const r=document.createElement("div");return r.className="foisit-form-error",r.style.display="none",r};(e??[]).forEach(r=>{const p=document.createElement("div");p.className="foisit-form-group";const g=r.description||r.name;p.appendChild(a(g,r.required));let m;if(r.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 w=h=>{(h??[]).forEach(x=>{const b=document.createElement("option");b.value=String(x.value??x.label??""),b.textContent=String(x.label??x.value??""),l.appendChild(b)})};if(Array.isArray(r.options)&&r.options.length)w(r.options);else if(typeof r.getOptions=="function"){const h=r.getOptions,x=document.createElement("option");x.value="",x.textContent="Loading...",l.appendChild(x),Promise.resolve().then(()=>h()).then(b=>{for(;l.options.length>1;)l.remove(1);w(b)}).catch(()=>{for(;l.options.length>1;)l.remove(1);const b=document.createElement("option");b.value="",b.textContent="Error loading options",l.appendChild(b)})}r.defaultValue!=null&&(l.value=String(r.defaultValue)),m=l}else if(r.type==="file"){const l=r,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 w=Array.from(y.files||[]),h=C;if(h.style.display="none",h.textContent="",w.length===0)return;const x=l.maxFiles??(l.multiple?10:1);if(w.length>x){h.textContent=`Please select at most ${x} file(s).`,h.style.display="block";return}const b=l.maxSizeBytes??1/0,u=w.reduce((v,A)=>v+A.size,0);if(w.some(v=>v.size>b)){h.textContent=`One or more files exceed the maximum size of ${Math.round(b/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(!w.every(L=>L.type?v.some(M=>M.startsWith(".")?L.name.toLowerCase().endsWith(M.toLowerCase()):L.type===M||L.type.startsWith(M.split("/")[0]+"/")):!0)){h.textContent="One or more files have an unsupported type.",h.style.display="block";return}}}),m=y}else{const l=document.createElement("input");l.className="foisit-form-input",r.type==="string"&&(l.placeholder=r.placeholder||"Type here..."),r.type==="number"?(l.type="number",typeof r.min=="number"&&(l.min=String(r.min)),typeof r.max=="number"&&(l.max=String(r.max)),typeof r.step=="number"&&(l.step=String(r.step)),r.defaultValue!=null&&(l.value=String(r.defaultValue))):r.type==="date"?(l.type="date",typeof r.min=="string"&&(l.min=r.min),typeof r.max=="string"&&(l.max=r.max),r.defaultValue!=null&&(l.value=String(r.defaultValue))):(l.type="text",r.defaultValue!=null&&(l.value=String(r.defaultValue))),m=l}const C=o();p.appendChild(m),p.appendChild(C),n.push({name:r.name,type:r.type,el:m,required:r.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 r=>{r.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 w=m.el.parentElement,h=w==null?void 0:w.querySelector(".foisit-form-error"),x=m.el,b=Array.from(x.files||[]);if(m.required&&b.length===0){g=!0,x.classList.add("foisit-error-border"),h&&(h.textContent="This file is required",h.style.display="block");continue}if(b.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(b[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(b[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?b:b[0];else if(k==="base64")try{const v=await Promise.all(b.map(A=>this.readFileAsDataURL(A)));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 C=(m.el.value??"").toString().trim(),l=m.el.parentElement,y=l==null?void 0:l.querySelector(".foisit-form-error");if(m.required&&(C==null||C==="")){g=!0,m.el.classList.add("foisit-error-border"),y&&(y.textContent="This field is required",y.style.display="block");continue}if(C!=="")if(m.type==="number"){const w=Number(C);Number.isNaN(w)||(p[m.name]=w)}else p[m.name]=C}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)}animateMessageEntrance(t,e){if(!t)return;t.style.transition=`opacity ${e}ms cubic-bezier(0.22, 0.9, 0.32, 1), transform ${Math.max(120,e)}ms cubic-bezier(0.22, 0.9, 0.32, 1)`,requestAnimationFrame(()=>{t.style.opacity="1",t.style.transform="translateY(0)"});const i=()=>{try{t.style.transition=""}catch{}t.removeEventListener("transitionend",i)};t.addEventListener("transitionend",i)}animateScrollToBottom(t){if(!this.messagesContainer)return;const e=this.messagesContainer,i=e.scrollTop,s=e.scrollHeight-e.clientHeight;if(s<=i||t<=0){e.scrollTop=s;return}const n=s-i,a=performance.now(),o=c=>{const f=Math.min(1,(c-a)/t),r=1-Math.pow(1-f,3);e.scrollTop=Math.round(i+n*r),f<1&&requestAnimationFrame(o)};requestAnimationFrame(o)}destroy(){var t;this.removeClickOutsideListener(),(t=this.container)==null||t.remove(),this.container=null,this.chatWindow=null,this.messagesContainer=null,this.input=null,this.isOpen=!1}escapeHtml(t){const e={"&":"&","<":"<",">":">",'"':""","'":"'"};return t.replace(/[&<>"']/g,i=>e[i])}renderMarkdown(t){let e=this.escapeHtml(t);return e=e.replace(/```(\w*)\n([\s\S]*?)```/g,(i,s,n)=>`<pre class="foisit-code-block"><code${s?` class="language-${s}"`:""}>${n.trim()}</code></pre>`),e=e.replace(/`([^`]+)`/g,'<code class="foisit-inline-code">$1</code>'),e=e.replace(/^###### (.+)$/gm,'<h6 class="foisit-md-h6">$1</h6>'),e=e.replace(/^##### (.+)$/gm,'<h5 class="foisit-md-h5">$1</h5>'),e=e.replace(/^#### (.+)$/gm,'<h4 class="foisit-md-h4">$1</h4>'),e=e.replace(/^### (.+)$/gm,'<h3 class="foisit-md-h3">$1</h3>'),e=e.replace(/^## (.+)$/gm,'<h2 class="foisit-md-h2">$1</h2>'),e=e.replace(/^# (.+)$/gm,'<h1 class="foisit-md-h1">$1</h1>'),e=e.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),e=e.replace(/__([^_]+)__/g,"<strong>$1</strong>"),e=e.replace(/\*([^*]+)\*/g,"<em>$1</em>"),e=e.replace(new RegExp("(?<!_)_([^_]+)_(?!_)","g"),"<em>$1</em>"),e=e.replace(/~~([^~]+)~~/g,"<del>$1</del>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer" class="foisit-md-link">$1</a>'),e=e.replace(/^[\-\*] (.+)$/gm,'<li class="foisit-md-li">$1</li>'),e=e.replace(/(<li class="foisit-md-li">.*<\/li>\n?)+/g,i=>`<ul class="foisit-md-ul">${i}</ul>`),e=e.replace(/^\d+\. (.+)$/gm,'<li class="foisit-md-li">$1</li>'),e=e.replace(new RegExp('(?<!<\\/ul>)(<li class="foisit-md-li">.*<\\/li>\\n?)+',"g"),i=>i.includes("<ul")?i:`<ol class="foisit-md-ol">${i}</ol>`),e=e.replace(/^> (.+)$/gm,'<blockquote class="foisit-md-blockquote">$1</blockquote>'),e=e.replace(/^(---|___|\*\*\*)$/gm,'<hr class="foisit-md-hr">'),e=e.replace(/\n\n+/g,'</p><p class="foisit-md-p">'),e=e.replace(/\n/g,"<br>"),e.match(/^<(h[1-6]|ul|ol|pre|blockquote|hr|p)/)||(e=`<p class="foisit-md-p">${e}</p>`),e}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=`
|
|
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,89 @@
|
|
|
352
352
|
transition: transform 0.2s;
|
|
353
353
|
}
|
|
354
354
|
.foisit-floating-btn:hover { transform: scale(1.05); }
|
|
355
|
-
|
|
355
|
+
|
|
356
|
+
/* Markdown Styles */
|
|
357
|
+
.foisit-bubble.system .foisit-md-p { margin: 0 0 0.5em 0; }
|
|
358
|
+
.foisit-bubble.system .foisit-md-p:last-child { margin-bottom: 0; }
|
|
359
|
+
|
|
360
|
+
.foisit-bubble.system .foisit-md-h1,
|
|
361
|
+
.foisit-bubble.system .foisit-md-h2,
|
|
362
|
+
.foisit-bubble.system .foisit-md-h3,
|
|
363
|
+
.foisit-bubble.system .foisit-md-h4,
|
|
364
|
+
.foisit-bubble.system .foisit-md-h5,
|
|
365
|
+
.foisit-bubble.system .foisit-md-h6 {
|
|
366
|
+
margin: 0.8em 0 0.4em 0;
|
|
367
|
+
font-weight: 600;
|
|
368
|
+
line-height: 1.3;
|
|
369
|
+
}
|
|
370
|
+
.foisit-bubble.system .foisit-md-h1:first-child,
|
|
371
|
+
.foisit-bubble.system .foisit-md-h2:first-child,
|
|
372
|
+
.foisit-bubble.system .foisit-md-h3:first-child { margin-top: 0; }
|
|
373
|
+
|
|
374
|
+
.foisit-bubble.system .foisit-md-h1 { font-size: 1.4em; }
|
|
375
|
+
.foisit-bubble.system .foisit-md-h2 { font-size: 1.25em; }
|
|
376
|
+
.foisit-bubble.system .foisit-md-h3 { font-size: 1.1em; }
|
|
377
|
+
.foisit-bubble.system .foisit-md-h4 { font-size: 1em; }
|
|
378
|
+
.foisit-bubble.system .foisit-md-h5 { font-size: 0.95em; }
|
|
379
|
+
.foisit-bubble.system .foisit-md-h6 { font-size: 0.9em; opacity: 0.85; }
|
|
380
|
+
|
|
381
|
+
.foisit-bubble.system .foisit-md-ul,
|
|
382
|
+
.foisit-bubble.system .foisit-md-ol {
|
|
383
|
+
margin: 0.5em 0;
|
|
384
|
+
padding-left: 1.5em;
|
|
385
|
+
}
|
|
386
|
+
.foisit-bubble.system .foisit-md-li { margin: 0.25em 0; }
|
|
387
|
+
|
|
388
|
+
.foisit-bubble.system .foisit-code-block {
|
|
389
|
+
background: rgba(0,0,0,0.15);
|
|
390
|
+
border-radius: 6px;
|
|
391
|
+
padding: 10px 12px;
|
|
392
|
+
margin: 0.5em 0;
|
|
393
|
+
overflow-x: auto;
|
|
394
|
+
font-family: 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
|
395
|
+
font-size: 0.85em;
|
|
396
|
+
line-height: 1.4;
|
|
397
|
+
}
|
|
398
|
+
.foisit-bubble.system .foisit-code-block code {
|
|
399
|
+
background: transparent;
|
|
400
|
+
padding: 0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.foisit-bubble.system .foisit-inline-code {
|
|
404
|
+
background: rgba(0,0,0,0.1);
|
|
405
|
+
padding: 2px 6px;
|
|
406
|
+
border-radius: 4px;
|
|
407
|
+
font-family: 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
|
408
|
+
font-size: 0.9em;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.foisit-bubble.system .foisit-md-blockquote {
|
|
412
|
+
border-left: 3px solid rgba(127,127,127,0.4);
|
|
413
|
+
margin: 0.5em 0;
|
|
414
|
+
padding-left: 12px;
|
|
415
|
+
opacity: 0.9;
|
|
416
|
+
font-style: italic;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.foisit-bubble.system .foisit-md-link {
|
|
420
|
+
color: inherit;
|
|
421
|
+
text-decoration: underline;
|
|
422
|
+
opacity: 0.9;
|
|
423
|
+
}
|
|
424
|
+
.foisit-bubble.system .foisit-md-link:hover { opacity: 1; }
|
|
425
|
+
|
|
426
|
+
.foisit-bubble.system .foisit-md-hr {
|
|
427
|
+
border: none;
|
|
428
|
+
border-top: 1px solid rgba(127,127,127,0.3);
|
|
429
|
+
margin: 0.8em 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.foisit-bubble.system strong { font-weight: 600; }
|
|
433
|
+
.foisit-bubble.system em { font-style: italic; }
|
|
434
|
+
.foisit-bubble.system del { text-decoration: line-through; opacity: 0.7; }
|
|
435
|
+
|
|
436
|
+
@media (prefers-color-scheme: dark) {
|
|
437
|
+
.foisit-bubble.system .foisit-code-block { background: rgba(255,255,255,0.08); }
|
|
438
|
+
.foisit-bubble.system .foisit-inline-code { background: rgba(255,255,255,0.1); }
|
|
439
|
+
}
|
|
440
|
+
`,document.head.appendChild(t)}}class q{constructor(t){this.config=t,this.isActivated=!1,this.lastProcessedInput="",this.processingLock=!1,this.defaultIntroMessage="How can I help you?",this.commandHandler=new z({enableSmartIntent:this.config.enableSmartIntent!==!1,intentEndpoint:this.config.intentEndpoint}),this.fallbackHandler=new W,typeof window<"u"&&typeof document<"u"?(this.voiceProcessor=new B,this.textToSpeech=new P,this.stateManager=new _,this.gestureHandler=new R,this.overlayManager=new G({floatingButton:this.config.floatingButton,inputPlaceholder:this.config.inputPlaceholder,enableGestureActivation:this.config.enableGestureActivation}),this.overlayManager.setExternalCommandExecutor(async e=>this.commandHandler.executeCommand(e)),this.config.commands.forEach(e=>this.commandHandler.addCommand(e)),this.config.fallbackResponse&&this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse),this.overlayManager.registerCallbacks(async e=>{if(typeof e=="string"){this.overlayManager.addMessage(e,"user"),await this.handleCommand(e);return}if(e&&typeof e=="object"){const i=e,s=i.label??i.commandId??"Selection";this.overlayManager.addMessage(String(s),"user"),this.overlayManager.showLoading();const n=await this.commandHandler.executeCommand(i);this.overlayManager.hideLoading(),this.processResponse(n)}},()=>console.log("AssistantService: Overlay closed."))):(this.voiceProcessor=void 0,this.textToSpeech=void 0,this.stateManager=void 0,this.gestureHandler=void 0,this.overlayManager=void 0,this.config.commands.forEach(e=>this.commandHandler.addCommand(e)),this.config.fallbackResponse&&this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse))}startListening(){if(typeof window>"u"||!this.voiceProcessor){console.log("AssistantService: Voice is disabled or unavailable; startListening() is a no-op.");return}}stopListening(){if(typeof window>"u"||!this.voiceProcessor){console.log("AssistantService: Voice unavailable; stopListening() is a no-op."),this.isActivated=!1;return}this.voiceProcessor.stopListening(),this.isActivated=!1}reactivate(){console.log("AssistantService: Reactivating assistant..."),this.isActivated=!1;try{this.startListening()}catch{}}async processActivation(t){var i;const e=(i=this.config.activationCommand)==null?void 0:i.toLowerCase();e&&(t===e?(console.log("AssistantService: Activation matched."),this.isActivated=!0,this.textToSpeech.speak(this.config.introMessage||this.defaultIntroMessage)):console.log("AssistantService: Activation command not recognized."))}async handleCommand(t){this.overlayManager.showLoading();let e;try{e=await this.commandHandler.executeCommand(t)}finally{this.overlayManager.hideLoading()}if(e.type==="form"&&e.fields){this.overlayManager.addForm(e.message,e.fields,async i=>{this.overlayManager.showLoading();let s;try{s=await this.commandHandler.executeCommand(i)}finally{this.overlayManager.hideLoading()}this.processResponse(s)});return}if(e.type==="error"){this.fallbackHandler.handleFallback(t),this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(),"system");return}if((e.type==="ambiguous"||e.type==="confirm")&&e.options){e.message&&this.overlayManager.addMessage(e.message,"system"),this.overlayManager.addOptions(e.options);return}e.message&&this.overlayManager.addMessage(e.message,"system")}destroy(){this.voiceProcessor.stopListening(),this.gestureHandler.destroy(),this.overlayManager.destroy()}processResponse(t){if(t){if(t.type==="form"&&t.fields){this.overlayManager.addForm(t.message,t.fields,async e=>{this.overlayManager.showLoading();let i;try{i=await this.commandHandler.executeCommand(e)}finally{this.overlayManager.hideLoading()}this.processResponse(i)});return}if((t.type==="ambiguous"||t.type==="confirm")&&t.options){t.message&&this.overlayManager.addMessage(t.message,"system"),this.overlayManager.addOptions(t.options);return}t.message&&this.overlayManager.addMessage(t.message,"system")}}addCommand(t,e){console.log(typeof t=="string"?`AssistantService: Adding command "${t}".`:`AssistantService: Adding rich command "${t.command}".`),this.commandHandler.addCommand(t,e)}removeCommand(t){console.log(`AssistantService: Removing command "${t}".`),this.commandHandler.removeCommand(t)}registerCommandHandler(t,e){this.overlayManager&&this.overlayManager.registerCommandHandler(t,e)}unregisterCommandHandler(t){this.overlayManager&&this.overlayManager.unregisterCommandHandler(t)}async runCommand(t){if(!this.overlayManager)throw new Error("Overlay manager not available.");const e=await this.overlayManager.runCommand(t);return e&&typeof e=="object"&&"type"in e&&this.processResponse(e),e}getCommands(){return this.commandHandler.getCommands()}toggle(t,e){console.log("AssistantService: Toggling overlay..."),this.overlayManager.toggle(async i=>{if(typeof i=="string"){this.overlayManager.addMessage(i,"user"),t&&t(i),await this.handleCommand(i);return}if(i&&typeof i=="object"){const s=i,n=s.label??s.commandId??"Selection";this.overlayManager.addMessage(String(n),"user"),this.overlayManager.showLoading();let a;try{a=await this.commandHandler.executeCommand(s)}finally{this.overlayManager.hideLoading()}this.processResponse(a)}},()=>{console.log("AssistantService: Overlay closed."),e&&e()})}}const T=S.createContext(null);let I=null;const Y=({config:d,children:t})=>{const[e,i]=S.useState(null),[s,n]=S.useState(!1);return S.useEffect(()=>{I?console.warn("Multiple AssistantProvider instances detected. Reusing global AssistantService."):(console.log("Initializing global AssistantService..."),I=new q(d));const a=I;return i(a),n(!0),()=>{var o;console.log("Cleaning up AssistantService..."),(o=a.destroy)==null||o.call(a),I=null}},[d]),!s||!e?E.jsx("div",{children:"Loading Assistant..."}):E.jsx(T.Provider,{value:e,children:t})},F=()=>{const d=S.useContext(T);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 E.jsx("button",{onClick:i,className:"assistant-activator",children:d})},X=d=>{const[t,e]=S.useState(d.getState());return S.useEffect(()=>{const i=s=>{e(s)};return d.subscribe(i),()=>{d.subscribe(()=>{})}},[d]),t};exports.AssistantActivator=K;exports.AssistantContext=T;exports.AssistantProvider=Y;exports.AssistantService=q;exports.ReactWrapper=N;exports.useAssistant=F;exports.useAssistantState=X;
|