@foisit/angular-wrapper 2.4.66 → 2.4.655
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.
|
@@ -1,306 +1,2138 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
// dist/libs/angular-wrapper/fesm2022/foisit-angular-wrapper.mjs
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { Component, Inject, Injectable, NgModule } from "@angular/core";
|
|
4
|
+
import { CommonModule } from "@angular/common";
|
|
5
|
+
|
|
6
|
+
// dist/libs/core/src/lib/command/command-handler.js
|
|
7
|
+
import { __awaiter as __awaiter2 } from "tslib";
|
|
8
|
+
|
|
9
|
+
// dist/libs/core/src/lib/ai/openai.service.js
|
|
10
|
+
import { __awaiter } from "tslib";
|
|
11
|
+
var OpenAIService = class {
|
|
12
|
+
constructor(endpoint) {
|
|
13
|
+
this.endpoint = endpoint || "https://foisit-ninja.netlify.app/.netlify/functions/intent";
|
|
14
|
+
}
|
|
15
|
+
determineIntent(userInput, availableCommands, context) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
try {
|
|
18
|
+
const payload = {
|
|
19
|
+
userInput,
|
|
20
|
+
commands: availableCommands.map((cmd) => ({
|
|
21
|
+
id: cmd.id,
|
|
22
|
+
command: cmd.command,
|
|
23
|
+
description: cmd.description,
|
|
24
|
+
parameters: cmd.parameters
|
|
25
|
+
// Send param schemas to AI
|
|
26
|
+
})),
|
|
27
|
+
context
|
|
28
|
+
};
|
|
29
|
+
const response = yield fetch(this.endpoint, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json"
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(payload)
|
|
34
35
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
38
|
-
this.voiceProcessor = new VoiceProcessor();
|
|
39
|
-
this.textToSpeech = new TextToSpeech();
|
|
40
|
-
this.gestureHandler = new GestureHandler();
|
|
41
|
-
this.overlayManager = new OverlayManager({
|
|
42
|
-
floatingButton: this.config.floatingButton,
|
|
43
|
-
inputPlaceholder: this.config.inputPlaceholder,
|
|
44
|
-
});
|
|
45
|
-
// Register global callbacks for floating button when overlay exists
|
|
46
|
-
this.overlayManager.registerCallbacks(async (input) => {
|
|
47
|
-
if (typeof input === 'string') {
|
|
48
|
-
this.overlayManager.addMessage(input, 'user');
|
|
49
|
-
}
|
|
50
|
-
else if (input && typeof input === 'object') {
|
|
51
|
-
const label = this.extractUserLabel(input);
|
|
52
|
-
if (label) {
|
|
53
|
-
this.overlayManager.addMessage(label, 'user');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
await this.handleCommand(input);
|
|
57
|
-
}, () => console.log('AssistantService: Overlay closed.'));
|
|
58
|
-
// Setup double-tap/double-click listener
|
|
59
|
-
this.stateManager = new StateManager();
|
|
60
|
-
this.gestureHandler.setupDoubleTapListener(() => this.toggle());
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
// Server environment: keep browser-specific properties null
|
|
64
|
-
this.stateManager = undefined;
|
|
65
|
-
this.voiceProcessor = undefined;
|
|
66
|
-
this.textToSpeech = undefined;
|
|
67
|
-
this.gestureHandler = undefined;
|
|
68
|
-
this.overlayManager = undefined;
|
|
69
|
-
}
|
|
70
|
-
// Setup commands from config
|
|
71
|
-
this.config.commands.forEach((cmd) => this.commandHandler.addCommand(cmd));
|
|
72
|
-
// Setup fallback response
|
|
73
|
-
if (this.config.fallbackResponse) {
|
|
74
|
-
this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse);
|
|
75
|
-
}
|
|
76
|
-
// Voice disabled for text-first pivot
|
|
77
|
-
// this.startListening();
|
|
78
|
-
// (moved into the browser-only block)
|
|
79
|
-
}
|
|
80
|
-
/** Start listening for activation and commands */
|
|
81
|
-
startListening() {
|
|
82
|
-
// No-op on server or when voice features are unavailable
|
|
83
|
-
if (typeof window === 'undefined' || !this.voiceProcessor) {
|
|
84
|
-
console.log('AssistantService: Voice is disabled or unavailable; startListening() is a no-op.');
|
|
85
|
-
return;
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error(`Proxy API Error: ${response.statusText}`);
|
|
86
38
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
39
|
+
const result = yield response.json();
|
|
40
|
+
return result;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("OpenAIService Error:", error);
|
|
43
|
+
return { type: "unknown" };
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// dist/libs/core/src/lib/command/command-handler.js
|
|
50
|
+
var CommandHandler = class {
|
|
51
|
+
constructor(arg = true) {
|
|
52
|
+
var _a;
|
|
53
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
54
|
+
this.openAIService = null;
|
|
55
|
+
this.context = null;
|
|
56
|
+
this.pendingConfirmation = null;
|
|
57
|
+
this.enableSmartIntent = true;
|
|
58
|
+
this.selectOptionsCache = /* @__PURE__ */ new Map();
|
|
59
|
+
if (typeof arg === "boolean") {
|
|
60
|
+
this.enableSmartIntent = arg;
|
|
61
|
+
if (this.enableSmartIntent) {
|
|
62
|
+
this.openAIService = new OpenAIService();
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.enableSmartIntent = (_a = arg.enableSmartIntent) !== null && _a !== void 0 ? _a : true;
|
|
67
|
+
if (this.enableSmartIntent) {
|
|
68
|
+
this.openAIService = new OpenAIService(arg.intentEndpoint);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Add a new command (string or object) */
|
|
72
|
+
addCommand(commandOrObj, action) {
|
|
73
|
+
let cmd;
|
|
74
|
+
if (typeof commandOrObj === "string") {
|
|
75
|
+
if (!action) {
|
|
76
|
+
throw new Error("Action required when adding command by string.");
|
|
77
|
+
}
|
|
78
|
+
cmd = {
|
|
79
|
+
id: commandOrObj.toLowerCase().replace(/\s+/g, "_"),
|
|
80
|
+
command: commandOrObj.toLowerCase(),
|
|
81
|
+
action
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
cmd = Object.assign({}, commandOrObj);
|
|
85
|
+
if (!cmd.id) {
|
|
86
|
+
cmd.id = cmd.command.toLowerCase().replace(/\s+/g, "_");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.commands.set(cmd.command.toLowerCase(), cmd);
|
|
90
|
+
if (cmd.id && cmd.id !== cmd.command) {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Remove an existing command */
|
|
94
|
+
removeCommand(command) {
|
|
95
|
+
this.commands.delete(command.toLowerCase());
|
|
96
|
+
}
|
|
97
|
+
/** Execute a command by matching input */
|
|
98
|
+
executeCommand(input) {
|
|
99
|
+
return __awaiter2(this, void 0, void 0, function* () {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
if (typeof input === "object" && input !== null) {
|
|
102
|
+
if (this.isStructured(input)) {
|
|
103
|
+
const cmdId = String(input.commandId);
|
|
104
|
+
const rawParams = (_a = input.params) !== null && _a !== void 0 ? _a : {};
|
|
105
|
+
const cmd2 = this.getCommandById(cmdId);
|
|
106
|
+
if (!cmd2)
|
|
107
|
+
return { message: "That command is not available.", type: "error" };
|
|
108
|
+
const params = this.sanitizeParamsForCommand(cmd2, rawParams);
|
|
109
|
+
const requiredParams = ((_b = cmd2.parameters) !== null && _b !== void 0 ? _b : []).filter((p) => p.required);
|
|
110
|
+
const missing = requiredParams.filter((p) => params[p.name] == null || params[p.name] === "");
|
|
111
|
+
if (missing.length > 0) {
|
|
112
|
+
this.context = { commandId: this.getCommandIdentifier(cmd2), params };
|
|
113
|
+
return {
|
|
114
|
+
message: `Please provide the required details for "${cmd2.command}".`,
|
|
115
|
+
type: "form",
|
|
116
|
+
fields: missing
|
|
117
|
+
};
|
|
101
118
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
if (cmd2.critical) {
|
|
120
|
+
this.pendingConfirmation = { commandId: this.getCommandIdentifier(cmd2), params };
|
|
121
|
+
return this.buildConfirmResponse(cmd2);
|
|
122
|
+
}
|
|
123
|
+
return this.safeRunAction(cmd2, params);
|
|
124
|
+
}
|
|
125
|
+
if (!this.context) {
|
|
126
|
+
return { message: "Session expired or invalid context.", type: "error" };
|
|
127
|
+
}
|
|
128
|
+
const cmd = this.getCommandById(this.context.commandId);
|
|
129
|
+
if (!cmd) {
|
|
130
|
+
this.context = null;
|
|
131
|
+
return { message: "Session expired or invalid context.", type: "error" };
|
|
132
|
+
}
|
|
133
|
+
if (Array.isArray(input)) {
|
|
134
|
+
return { message: "Invalid form payload.", type: "error" };
|
|
135
|
+
}
|
|
136
|
+
const mergedParams = Object.assign(Object.assign({}, this.context.params), input);
|
|
137
|
+
if (cmd.critical) {
|
|
138
|
+
this.context = null;
|
|
139
|
+
this.pendingConfirmation = {
|
|
140
|
+
commandId: this.getCommandIdentifier(cmd),
|
|
141
|
+
params: mergedParams
|
|
142
|
+
};
|
|
143
|
+
return this.buildConfirmResponse(cmd);
|
|
144
|
+
}
|
|
145
|
+
const result = yield this.safeRunAction(cmd, mergedParams);
|
|
146
|
+
this.context = null;
|
|
147
|
+
return this.normalizeResponse(result);
|
|
148
|
+
}
|
|
149
|
+
const trimmedInput = input.trim().toLowerCase();
|
|
150
|
+
if (this.pendingConfirmation) {
|
|
151
|
+
const normalized = trimmedInput;
|
|
152
|
+
if (["yes", "y", "confirm", "ok", "okay"].includes(normalized)) {
|
|
153
|
+
const { commandId, params } = this.pendingConfirmation;
|
|
154
|
+
this.pendingConfirmation = null;
|
|
155
|
+
const cmd = this.getCommandById(commandId);
|
|
156
|
+
if (!cmd) {
|
|
157
|
+
return { message: "That action is no longer available.", type: "error" };
|
|
108
158
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
return this.safeRunAction(cmd, params);
|
|
160
|
+
}
|
|
161
|
+
if (["no", "n", "cancel", "stop"].includes(normalized)) {
|
|
162
|
+
this.pendingConfirmation = null;
|
|
163
|
+
return { message: "Cancelled.", type: "success" };
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
message: "Please confirm: Yes or No.",
|
|
167
|
+
type: "confirm",
|
|
168
|
+
options: [
|
|
169
|
+
{ label: "Yes", value: "yes" },
|
|
170
|
+
{ label: "No", value: "no" }
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const exactCommand = this.commands.get(trimmedInput);
|
|
175
|
+
if (exactCommand) {
|
|
176
|
+
const cmd = exactCommand;
|
|
177
|
+
const requiredParams = ((_c = cmd.parameters) !== null && _c !== void 0 ? _c : []).filter((p) => p.required);
|
|
178
|
+
if (requiredParams.length > 0) {
|
|
179
|
+
this.context = { commandId: this.getCommandIdentifier(cmd), params: {} };
|
|
180
|
+
return {
|
|
181
|
+
message: `Please provide the required details for "${cmd.command}".`,
|
|
182
|
+
type: "form",
|
|
183
|
+
fields: requiredParams
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (cmd.critical) {
|
|
187
|
+
this.pendingConfirmation = { commandId: this.getCommandIdentifier(cmd), params: {} };
|
|
188
|
+
return this.buildConfirmResponse(cmd);
|
|
189
|
+
}
|
|
190
|
+
return this.safeRunAction(cmd, {});
|
|
191
|
+
}
|
|
192
|
+
const deterministic = yield this.tryDeterministicMatch(trimmedInput);
|
|
193
|
+
if (deterministic)
|
|
194
|
+
return deterministic;
|
|
195
|
+
if (this.enableSmartIntent && this.openAIService) {
|
|
196
|
+
const availableCommands = yield this.getCommandsForAI();
|
|
197
|
+
const aiResult = yield this.openAIService.determineIntent(trimmedInput, availableCommands, this.context);
|
|
198
|
+
return this.handleAIResult(aiResult);
|
|
199
|
+
}
|
|
200
|
+
if (!this.enableSmartIntent) {
|
|
201
|
+
return { message: "I'm not sure what you mean.", type: "error" };
|
|
202
|
+
}
|
|
203
|
+
return this.listAllCommands();
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
handleAIResult(result) {
|
|
207
|
+
return __awaiter2(this, void 0, void 0, function* () {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
if (result.type === "match" && result.match) {
|
|
210
|
+
const cmd = this.getCommandById(result.match);
|
|
211
|
+
if (!cmd) {
|
|
212
|
+
return { message: "I'm not sure what you mean.", type: "error" };
|
|
213
|
+
}
|
|
214
|
+
const rawParams = (_a = result.params) !== null && _a !== void 0 ? _a : {};
|
|
215
|
+
const sanitizedParams = this.sanitizeParamsForCommand(cmd, rawParams);
|
|
216
|
+
const params = cmd.allowAiParamExtraction === false ? {} : sanitizedParams;
|
|
217
|
+
const requiredParams = ((_b = cmd.parameters) !== null && _b !== void 0 ? _b : []).filter((p) => p.required);
|
|
218
|
+
const missingRequired = requiredParams.filter((p) => params[p.name] == null || params[p.name] === "");
|
|
219
|
+
if (result.incomplete || missingRequired.length > 0) {
|
|
220
|
+
this.context = { commandId: this.getCommandIdentifier(cmd), params };
|
|
221
|
+
const mustUseForm = cmd.collectRequiredViaForm !== false;
|
|
222
|
+
const askSingle = !mustUseForm && this.shouldAskSingleQuestion(missingRequired);
|
|
223
|
+
if (askSingle) {
|
|
224
|
+
const names = missingRequired.map((p) => p.name).join(" and ");
|
|
225
|
+
return {
|
|
226
|
+
message: result.message || `Please provide ${names}.`,
|
|
227
|
+
type: "question"
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
message: result.message || `Please fill in the missing details for "${cmd.command}".`,
|
|
232
|
+
type: "form",
|
|
233
|
+
fields: missingRequired
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (cmd.critical) {
|
|
237
|
+
this.pendingConfirmation = {
|
|
238
|
+
commandId: this.getCommandIdentifier(cmd),
|
|
239
|
+
params
|
|
240
|
+
};
|
|
241
|
+
return this.buildConfirmResponse(cmd);
|
|
242
|
+
}
|
|
243
|
+
const actionResult = yield cmd.action(params);
|
|
244
|
+
return this.normalizeResponse(actionResult);
|
|
245
|
+
}
|
|
246
|
+
if (result.type === "ambiguous" && result.options && result.options.length) {
|
|
247
|
+
return {
|
|
248
|
+
message: result.message || "Did you mean one of these?",
|
|
249
|
+
type: "ambiguous",
|
|
250
|
+
options: result.options.map((o) => {
|
|
251
|
+
var _a2;
|
|
252
|
+
return {
|
|
253
|
+
label: o.label,
|
|
254
|
+
value: (_a2 = o.commandId) !== null && _a2 !== void 0 ? _a2 : o.label,
|
|
255
|
+
commandId: o.commandId
|
|
256
|
+
};
|
|
257
|
+
})
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return this.listAllCommands();
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
sanitizeParamsForCommand(cmd, params) {
|
|
264
|
+
var _a, _b;
|
|
265
|
+
const sanitized = Object.assign({}, params !== null && params !== void 0 ? params : {});
|
|
266
|
+
for (const p of (_a = cmd.parameters) !== null && _a !== void 0 ? _a : []) {
|
|
267
|
+
const value = sanitized[p.name];
|
|
268
|
+
if (p.type === "string") {
|
|
269
|
+
if (typeof value !== "string") {
|
|
270
|
+
delete sanitized[p.name];
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const trimmed = value.trim();
|
|
274
|
+
if (!trimmed) {
|
|
275
|
+
delete sanitized[p.name];
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
sanitized[p.name] = trimmed;
|
|
279
|
+
}
|
|
280
|
+
if (p.type === "number") {
|
|
281
|
+
const numeric = typeof value === "number" ? value : Number(value === null || value === void 0 ? void 0 : value.toString().trim());
|
|
282
|
+
if (Number.isNaN(numeric)) {
|
|
283
|
+
delete sanitized[p.name];
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (typeof p.min === "number" && numeric < p.min) {
|
|
287
|
+
delete sanitized[p.name];
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (typeof p.max === "number" && numeric > p.max) {
|
|
291
|
+
delete sanitized[p.name];
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
sanitized[p.name] = numeric;
|
|
295
|
+
}
|
|
296
|
+
if (p.type === "date") {
|
|
297
|
+
const v = sanitized[p.name];
|
|
298
|
+
if (typeof v === "string") {
|
|
299
|
+
const s = v.trim();
|
|
300
|
+
if (!this.isIsoDateString(s)) {
|
|
301
|
+
delete sanitized[p.name];
|
|
117
302
|
} else {
|
|
118
|
-
|
|
303
|
+
sanitized[p.name] = s;
|
|
119
304
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.processingLock = true;
|
|
123
|
-
setTimeout(() => (this.processingLock = false), 1000);
|
|
124
|
-
});
|
|
125
|
-
*/
|
|
126
|
-
}
|
|
127
|
-
/** Stop listening for voice input */
|
|
128
|
-
stopListening() {
|
|
129
|
-
if (typeof window === 'undefined' || !this.voiceProcessor) {
|
|
130
|
-
console.log('AssistantService: Voice unavailable; stopListening() is a no-op.');
|
|
131
|
-
this.isActivated = false;
|
|
132
|
-
return;
|
|
305
|
+
} else {
|
|
306
|
+
delete sanitized[p.name];
|
|
133
307
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
this.isActivated = false;
|
|
141
|
-
try {
|
|
142
|
-
this.startListening();
|
|
308
|
+
}
|
|
309
|
+
if (p.type === "select") {
|
|
310
|
+
const v = typeof value === "string" ? value : value === null || value === void 0 ? void 0 : value.toString();
|
|
311
|
+
if (!v) {
|
|
312
|
+
delete sanitized[p.name];
|
|
313
|
+
continue;
|
|
143
314
|
}
|
|
144
|
-
|
|
145
|
-
|
|
315
|
+
if (Array.isArray(p.options) && p.options.length > 0) {
|
|
316
|
+
const allowed = p.options.some((opt) => String(opt.value) === String(v));
|
|
317
|
+
if (!allowed) {
|
|
318
|
+
delete sanitized[p.name];
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
sanitized[p.name] = v;
|
|
323
|
+
}
|
|
324
|
+
if (p.type === "file") {
|
|
325
|
+
const val = sanitized[p.name];
|
|
326
|
+
const isFileLike = val && typeof val === "object" && typeof val.name === "string" && typeof val.size === "number";
|
|
327
|
+
const isDataUrl = typeof val === "string" && /^data:[^;]+;base64,/.test(val);
|
|
328
|
+
const delivery = (_b = p.delivery) !== null && _b !== void 0 ? _b : "file";
|
|
329
|
+
if (delivery === "base64") {
|
|
330
|
+
if (!isDataUrl && !isFileLike) {
|
|
331
|
+
delete sanitized[p.name];
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
if (!isFileLike) {
|
|
335
|
+
delete sanitized[p.name];
|
|
336
|
+
}
|
|
146
337
|
}
|
|
338
|
+
}
|
|
147
339
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
340
|
+
return sanitized;
|
|
341
|
+
}
|
|
342
|
+
isIsoDateString(value) {
|
|
343
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
344
|
+
return false;
|
|
345
|
+
const dt = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
|
|
346
|
+
return !Number.isNaN(dt.getTime());
|
|
347
|
+
}
|
|
348
|
+
shouldAskSingleQuestion(missing) {
|
|
349
|
+
if (missing.length !== 1)
|
|
350
|
+
return false;
|
|
351
|
+
const t = missing[0].type;
|
|
352
|
+
return t === "string" || t === "number" || t === "date";
|
|
353
|
+
}
|
|
354
|
+
buildConfirmResponse(cmd) {
|
|
355
|
+
return {
|
|
356
|
+
message: `Are you sure you want to run "${cmd.command}"?`,
|
|
357
|
+
type: "confirm",
|
|
358
|
+
options: [
|
|
359
|
+
{ label: "Yes", value: "yes" },
|
|
360
|
+
{ label: "No", value: "no" }
|
|
361
|
+
]
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
tryDeterministicMatch(input) {
|
|
365
|
+
return __awaiter2(this, void 0, void 0, function* () {
|
|
366
|
+
var _a, _b;
|
|
367
|
+
const candidates = [];
|
|
368
|
+
for (const cmd2 of this.commands.values()) {
|
|
369
|
+
let score = 0;
|
|
370
|
+
const commandPhrase = cmd2.command.toLowerCase();
|
|
371
|
+
if (input.includes(commandPhrase))
|
|
372
|
+
score += 5;
|
|
373
|
+
const keywords = (_a = cmd2.keywords) !== null && _a !== void 0 ? _a : [];
|
|
374
|
+
for (const kw of keywords) {
|
|
375
|
+
const k = kw.toLowerCase().trim();
|
|
376
|
+
if (!k)
|
|
377
|
+
continue;
|
|
378
|
+
if (input === k)
|
|
379
|
+
score += 4;
|
|
380
|
+
else if (input.includes(k))
|
|
381
|
+
score += 3;
|
|
382
|
+
}
|
|
383
|
+
if (score > 0)
|
|
384
|
+
candidates.push({ cmd: cmd2, score });
|
|
385
|
+
}
|
|
386
|
+
if (candidates.length === 0)
|
|
387
|
+
return null;
|
|
388
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
389
|
+
const topScore = candidates[0].score;
|
|
390
|
+
const top = candidates.filter((c) => c.score === topScore).slice(0, 3);
|
|
391
|
+
if (top.length > 1) {
|
|
392
|
+
return {
|
|
393
|
+
message: "I think you mean one of these. Which one should I run?",
|
|
394
|
+
type: "ambiguous",
|
|
395
|
+
options: top.map((c) => ({
|
|
396
|
+
label: c.cmd.command,
|
|
397
|
+
value: c.cmd.command,
|
|
398
|
+
commandId: c.cmd.id
|
|
399
|
+
}))
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
const cmd = top[0].cmd;
|
|
403
|
+
const requiredParams = ((_b = cmd.parameters) !== null && _b !== void 0 ? _b : []).filter((p) => p.required);
|
|
404
|
+
if (requiredParams.length > 0) {
|
|
405
|
+
this.context = { commandId: this.getCommandIdentifier(cmd), params: {} };
|
|
406
|
+
return {
|
|
407
|
+
message: `Please provide the required details for "${cmd.command}".`,
|
|
408
|
+
type: "form",
|
|
409
|
+
fields: requiredParams
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (cmd.critical) {
|
|
413
|
+
this.pendingConfirmation = { commandId: this.getCommandIdentifier(cmd), params: {} };
|
|
414
|
+
return this.buildConfirmResponse(cmd);
|
|
415
|
+
}
|
|
416
|
+
return this.safeRunAction(cmd, {});
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
safeRunAction(cmd, params) {
|
|
420
|
+
return __awaiter2(this, void 0, void 0, function* () {
|
|
421
|
+
try {
|
|
422
|
+
const result = yield cmd.action(params !== null && params !== void 0 ? params : {});
|
|
423
|
+
return this.normalizeResponse(result);
|
|
424
|
+
} catch (_a) {
|
|
425
|
+
return { message: "Something went wrong while running that command.", type: "error" };
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
getCommandsForAI() {
|
|
430
|
+
return __awaiter2(this, void 0, void 0, function* () {
|
|
431
|
+
const commands = Array.from(this.commands.values()).map((cmd) => Object.assign(Object.assign({}, cmd), { parameters: cmd.parameters ? cmd.parameters.map((param) => Object.assign({}, param)) : void 0 }));
|
|
432
|
+
yield Promise.all(commands.map((cmd) => __awaiter2(this, void 0, void 0, function* () {
|
|
433
|
+
if (!cmd.parameters)
|
|
434
|
+
return;
|
|
435
|
+
yield Promise.all(cmd.parameters.map((p) => __awaiter2(this, void 0, void 0, function* () {
|
|
436
|
+
var _a;
|
|
437
|
+
if (p.type !== "select" || !p.getOptions || p.options && p.options.length)
|
|
153
438
|
return;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
439
|
+
const cacheKey = `${(_a = cmd.id) !== null && _a !== void 0 ? _a : cmd.command}:${p.name}`;
|
|
440
|
+
const cached = this.selectOptionsCache.get(cacheKey);
|
|
441
|
+
const now = Date.now();
|
|
442
|
+
if (cached && now - cached.ts < 6e4) {
|
|
443
|
+
p.options = cached.options;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
const opts = yield p.getOptions();
|
|
448
|
+
this.selectOptionsCache.set(cacheKey, { options: opts, ts: now });
|
|
449
|
+
p.options = opts;
|
|
450
|
+
} catch (_b) {
|
|
451
|
+
}
|
|
452
|
+
})));
|
|
453
|
+
})));
|
|
454
|
+
return commands;
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
getCommandById(id) {
|
|
458
|
+
for (const cmd of this.commands.values()) {
|
|
459
|
+
if (cmd.id === id)
|
|
460
|
+
return cmd;
|
|
461
|
+
}
|
|
462
|
+
return void 0;
|
|
463
|
+
}
|
|
464
|
+
listAllCommands() {
|
|
465
|
+
const options = Array.from(this.commands.values()).map((cmd) => {
|
|
466
|
+
var _a, _b;
|
|
467
|
+
return {
|
|
468
|
+
label: cmd.command,
|
|
469
|
+
value: (_a = cmd.id) !== null && _a !== void 0 ? _a : cmd.command,
|
|
470
|
+
commandId: (_b = cmd.id) !== null && _b !== void 0 ? _b : cmd.command
|
|
471
|
+
};
|
|
472
|
+
});
|
|
473
|
+
return {
|
|
474
|
+
message: "Here are the available commands:",
|
|
475
|
+
type: "ambiguous",
|
|
476
|
+
options
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
normalizeResponse(result) {
|
|
480
|
+
if (typeof result === "string") {
|
|
481
|
+
return { message: result, type: "success" };
|
|
482
|
+
}
|
|
483
|
+
if (result && typeof result === "object") {
|
|
484
|
+
return result;
|
|
485
|
+
}
|
|
486
|
+
return { message: "Done", type: "success" };
|
|
487
|
+
}
|
|
488
|
+
isStructured(input) {
|
|
489
|
+
return typeof input["commandId"] === "string";
|
|
490
|
+
}
|
|
491
|
+
getCommandIdentifier(cmd) {
|
|
492
|
+
if (!cmd.id) {
|
|
493
|
+
cmd.id = cmd.command.toLowerCase().replace(/\s+/g, "_");
|
|
494
|
+
}
|
|
495
|
+
return cmd.id;
|
|
496
|
+
}
|
|
497
|
+
/** List all registered commands */
|
|
498
|
+
getCommands() {
|
|
499
|
+
return Array.from(this.commands.keys());
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// dist/libs/core/src/lib/speech/text-to-speech.js
|
|
504
|
+
var TextToSpeech = class {
|
|
505
|
+
constructor() {
|
|
506
|
+
this.synth = typeof window !== "undefined" ? window.speechSynthesis : null;
|
|
507
|
+
}
|
|
508
|
+
speak(text, options) {
|
|
509
|
+
if (!this.synth) {
|
|
510
|
+
console.error("SpeechSynthesis API is not supported in this environment.");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
514
|
+
if (options) {
|
|
515
|
+
utterance.pitch = options.pitch || 1;
|
|
516
|
+
utterance.rate = options.rate || 1;
|
|
517
|
+
utterance.volume = options.volume || 1;
|
|
518
|
+
}
|
|
519
|
+
if (typeof window !== "undefined") {
|
|
520
|
+
utterance.onstart = () => {
|
|
521
|
+
window.dispatchEvent(new CustomEvent("foisit:tts-start"));
|
|
522
|
+
};
|
|
523
|
+
utterance.onend = () => {
|
|
524
|
+
console.log("Speech finished.");
|
|
525
|
+
window.dispatchEvent(new CustomEvent("foisit:tts-end"));
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
utterance.onerror = (event) => {
|
|
529
|
+
console.error("Error during speech synthesis:", event.error);
|
|
530
|
+
};
|
|
531
|
+
this.synth.speak(utterance);
|
|
532
|
+
}
|
|
533
|
+
stopSpeaking() {
|
|
534
|
+
if (this.synth) {
|
|
535
|
+
this.synth.cancel();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// dist/libs/core/src/lib/fallback/fallback-handler.js
|
|
541
|
+
var FallbackHandler = class {
|
|
542
|
+
constructor() {
|
|
543
|
+
this.fallbackMessage = "Sorry, I didn\u2019t understand that.";
|
|
544
|
+
}
|
|
545
|
+
setFallbackMessage(message) {
|
|
546
|
+
this.fallbackMessage = message;
|
|
547
|
+
}
|
|
548
|
+
handleFallback(transcript) {
|
|
549
|
+
if (transcript)
|
|
550
|
+
console.log(`Fallback triggered for: "${transcript}"`);
|
|
551
|
+
console.log(this.fallbackMessage);
|
|
552
|
+
new TextToSpeech().speak(this.fallbackMessage);
|
|
553
|
+
}
|
|
554
|
+
getFallbackMessage() {
|
|
555
|
+
return this.fallbackMessage;
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// dist/libs/core/src/lib/speech/voice-processor.js
|
|
560
|
+
import { __awaiter as __awaiter3 } from "tslib";
|
|
561
|
+
var getRecognitionCtor = () => {
|
|
562
|
+
var _a, _b;
|
|
563
|
+
if (typeof window === "undefined")
|
|
564
|
+
return null;
|
|
565
|
+
const w = window;
|
|
566
|
+
return (_b = (_a = w.SpeechRecognition) !== null && _a !== void 0 ? _a : w.webkitSpeechRecognition) !== null && _b !== void 0 ? _b : null;
|
|
567
|
+
};
|
|
568
|
+
var VoiceProcessor = class {
|
|
569
|
+
// Debug logger helpers
|
|
570
|
+
log(message) {
|
|
571
|
+
if (this.debugEnabled && message) {
|
|
572
|
+
console.log("[VoiceProcessor]", message);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
warn(message) {
|
|
576
|
+
if (this.debugEnabled && message) {
|
|
577
|
+
console.warn("[VoiceProcessor]", message);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
error(message) {
|
|
581
|
+
if (this.debugEnabled && message) {
|
|
582
|
+
console.error("[VoiceProcessor]", message);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
constructor(language = "en-US", options = {}) {
|
|
586
|
+
var _a, _b;
|
|
587
|
+
this.recognition = null;
|
|
588
|
+
this.isListening = false;
|
|
589
|
+
this.engineActive = false;
|
|
590
|
+
this.intentionallyStopped = false;
|
|
591
|
+
this.restartAllowed = true;
|
|
592
|
+
this.lastStart = 0;
|
|
593
|
+
this.backoffMs = 250;
|
|
594
|
+
this.destroyed = false;
|
|
595
|
+
this.resultCallback = null;
|
|
596
|
+
this.ttsSpeaking = false;
|
|
597
|
+
this.debugEnabled = true;
|
|
598
|
+
this.restartTimer = null;
|
|
599
|
+
this.prewarmed = false;
|
|
600
|
+
this.hadResultThisSession = false;
|
|
601
|
+
this.onTTSStart = () => {
|
|
602
|
+
var _a2;
|
|
603
|
+
this.ttsSpeaking = true;
|
|
604
|
+
try {
|
|
605
|
+
(_a2 = this.recognition) === null || _a2 === void 0 ? void 0 : _a2.stop();
|
|
606
|
+
} catch (_b2) {
|
|
607
|
+
}
|
|
608
|
+
if (this.isListening) {
|
|
609
|
+
this.emitStatus("speaking");
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
this.onTTSEnd = () => {
|
|
613
|
+
this.ttsSpeaking = false;
|
|
614
|
+
if (this.isListening && this.restartAllowed) {
|
|
615
|
+
this.safeRestart();
|
|
616
|
+
} else {
|
|
617
|
+
this.emitStatus(this.isListening ? "listening" : "idle");
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
const Ctor = getRecognitionCtor();
|
|
621
|
+
if (Ctor) {
|
|
622
|
+
this.recognition = new Ctor();
|
|
623
|
+
this.recognition.lang = language;
|
|
624
|
+
this.recognition.interimResults = (_a = options.interimResults) !== null && _a !== void 0 ? _a : true;
|
|
625
|
+
this.recognition.continuous = (_b = options.continuous) !== null && _b !== void 0 ? _b : true;
|
|
626
|
+
this.recognition.onresult = (event) => this.handleResult(event, options);
|
|
627
|
+
this.recognition.onend = () => this.handleEnd();
|
|
628
|
+
this.recognition.onstart = () => {
|
|
629
|
+
this.log("recognition onstart");
|
|
630
|
+
this.engineActive = true;
|
|
631
|
+
this.hadResultThisSession = false;
|
|
632
|
+
if (this.restartTimer) {
|
|
633
|
+
clearTimeout(this.restartTimer);
|
|
634
|
+
this.restartTimer = null;
|
|
159
635
|
}
|
|
160
|
-
|
|
161
|
-
|
|
636
|
+
this.backoffMs = 250;
|
|
637
|
+
if (this.isListening && !this.ttsSpeaking) {
|
|
638
|
+
this.emitStatus("listening");
|
|
162
639
|
}
|
|
640
|
+
};
|
|
641
|
+
const vrec = this.recognition;
|
|
642
|
+
vrec.onaudiostart = () => this.log("onaudiostart");
|
|
643
|
+
vrec.onsoundstart = () => this.log("onsoundstart");
|
|
644
|
+
vrec.onspeechstart = () => this.log("onspeechstart");
|
|
645
|
+
vrec.onspeechend = () => this.log("onspeechend");
|
|
646
|
+
vrec.onsoundend = () => this.log("onsoundend");
|
|
647
|
+
vrec.onaudioend = () => this.log("onaudioend");
|
|
648
|
+
this.recognition.onerror = (event) => this.handleError(event);
|
|
649
|
+
} else {
|
|
650
|
+
this.recognition = null;
|
|
651
|
+
this.emitStatus("unsupported");
|
|
163
652
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
653
|
+
if (typeof window !== "undefined") {
|
|
654
|
+
window.addEventListener("foisit:tts-start", this.onTTSStart);
|
|
655
|
+
window.addEventListener("foisit:tts-end", this.onTTSEnd);
|
|
656
|
+
this.visibilityHandler = () => {
|
|
657
|
+
var _a2;
|
|
658
|
+
if (typeof document !== "undefined" && document.hidden) {
|
|
659
|
+
try {
|
|
660
|
+
(_a2 = this.recognition) === null || _a2 === void 0 ? void 0 : _a2.stop();
|
|
661
|
+
} catch (_b2) {
|
|
662
|
+
}
|
|
663
|
+
this.emitStatus(this.ttsSpeaking ? "speaking" : "idle");
|
|
664
|
+
} else if (this.isListening && !this.ttsSpeaking) {
|
|
665
|
+
this.safeRestart();
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
if (typeof document !== "undefined") {
|
|
669
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
670
|
+
}
|
|
671
|
+
} else {
|
|
672
|
+
this.visibilityHandler = void 0;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/** Check if SpeechRecognition is available */
|
|
676
|
+
isSupported() {
|
|
677
|
+
return getRecognitionCtor() !== null;
|
|
678
|
+
}
|
|
679
|
+
/** Allow consumers (wrappers) to observe status changes */
|
|
680
|
+
onStatusChange(callback) {
|
|
681
|
+
this.statusCallback = callback;
|
|
682
|
+
}
|
|
683
|
+
/** Start listening for speech input */
|
|
684
|
+
startListening(callback) {
|
|
685
|
+
if (!this.isSupported() || !this.recognition) {
|
|
686
|
+
this.warn("VoiceProcessor: SpeechRecognition is not supported in this browser.");
|
|
687
|
+
this.emitStatus("unsupported");
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (this.isListening) {
|
|
691
|
+
this.warn("VoiceProcessor: Already listening.");
|
|
692
|
+
this.resultCallback = callback;
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
this.resultCallback = callback;
|
|
696
|
+
this.intentionallyStopped = false;
|
|
697
|
+
this.restartAllowed = true;
|
|
698
|
+
this.isListening = true;
|
|
699
|
+
this.emitStatus("listening");
|
|
700
|
+
this.prewarmAudio().finally(() => {
|
|
701
|
+
this.safeRestart();
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
/** Stop listening for speech input */
|
|
705
|
+
stopListening() {
|
|
706
|
+
var _a;
|
|
707
|
+
this.intentionallyStopped = true;
|
|
708
|
+
this.restartAllowed = false;
|
|
709
|
+
this.isListening = false;
|
|
710
|
+
this.emitStatus(this.ttsSpeaking ? "speaking" : "idle");
|
|
711
|
+
try {
|
|
712
|
+
(_a = this.recognition) === null || _a === void 0 ? void 0 : _a.stop();
|
|
713
|
+
} catch (_b) {
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/** Clean up listeners */
|
|
717
|
+
destroy() {
|
|
718
|
+
this.destroyed = true;
|
|
719
|
+
this.stopListening();
|
|
720
|
+
this.resultCallback = null;
|
|
721
|
+
window.removeEventListener("foisit:tts-start", this.onTTSStart);
|
|
722
|
+
window.removeEventListener("foisit:tts-end", this.onTTSEnd);
|
|
723
|
+
if (this.visibilityHandler) {
|
|
724
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
725
|
+
this.visibilityHandler = void 0;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
/** Handle recognized speech results */
|
|
729
|
+
handleResult(event, options) {
|
|
730
|
+
var _a, _b, _c, _d;
|
|
731
|
+
if (!this.resultCallback)
|
|
732
|
+
return;
|
|
733
|
+
const threshold = (_a = options.confidenceThreshold) !== null && _a !== void 0 ? _a : 0.6;
|
|
734
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
735
|
+
const res = event.results[i];
|
|
736
|
+
const alt = res && res[0];
|
|
737
|
+
const transcript = ((_c = (_b = alt === null || alt === void 0 ? void 0 : alt.transcript) === null || _b === void 0 ? void 0 : _b.trim) === null || _c === void 0 ? void 0 : _c.call(_b)) || "";
|
|
738
|
+
const confidence = (_d = alt === null || alt === void 0 ? void 0 : alt.confidence) !== null && _d !== void 0 ? _d : 0;
|
|
739
|
+
if (!transcript)
|
|
740
|
+
continue;
|
|
741
|
+
if (!res.isFinal && options.interimResults === false)
|
|
742
|
+
continue;
|
|
743
|
+
if (res.isFinal && confidence < threshold)
|
|
744
|
+
continue;
|
|
745
|
+
try {
|
|
746
|
+
this.hadResultThisSession = true;
|
|
747
|
+
this.resultCallback(transcript, !!res.isFinal);
|
|
748
|
+
} catch (_e) {
|
|
749
|
+
this.error("VoiceProcessor: result callback error");
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
/** Handle session end */
|
|
754
|
+
handleEnd() {
|
|
755
|
+
this.log("recognition onend");
|
|
756
|
+
this.engineActive = false;
|
|
757
|
+
if (this.destroyed || this.intentionallyStopped || !this.restartAllowed || this.ttsSpeaking) {
|
|
758
|
+
if (!this.ttsSpeaking) {
|
|
759
|
+
this.isListening = false;
|
|
760
|
+
this.emitStatus("idle");
|
|
761
|
+
}
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
this.isListening = true;
|
|
765
|
+
this.scheduleRestart();
|
|
766
|
+
}
|
|
767
|
+
/** Handle errors during speech recognition */
|
|
768
|
+
handleError(event) {
|
|
769
|
+
const err = event === null || event === void 0 ? void 0 : event.error;
|
|
770
|
+
this.warn(`Error occurred: ${err !== null && err !== void 0 ? err : "unknown"}`);
|
|
771
|
+
const fatal = ["not-allowed", "service-not-allowed", "bad-grammar", "language-not-supported"];
|
|
772
|
+
if (err && fatal.includes(err)) {
|
|
773
|
+
this.intentionallyStopped = true;
|
|
774
|
+
this.restartAllowed = false;
|
|
775
|
+
this.isListening = false;
|
|
776
|
+
this.emitStatus("error", { error: err });
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
this.scheduleRestart();
|
|
780
|
+
}
|
|
781
|
+
safeRestart() {
|
|
782
|
+
if (!this.recognition)
|
|
783
|
+
return;
|
|
784
|
+
if (this.engineActive) {
|
|
785
|
+
this.log("safeRestart: engine already active, skipping start");
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const now = Date.now();
|
|
789
|
+
if (now - this.lastStart < 300) {
|
|
790
|
+
setTimeout(() => this.safeRestart(), 300);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
this.lastStart = now;
|
|
794
|
+
try {
|
|
795
|
+
this.log("calling recognition.start()");
|
|
796
|
+
this.recognition.start();
|
|
797
|
+
this.backoffMs = 250;
|
|
798
|
+
if (this.isListening && !this.ttsSpeaking) {
|
|
799
|
+
this.emitStatus("listening");
|
|
800
|
+
}
|
|
801
|
+
} catch (_a) {
|
|
802
|
+
this.error("recognition.start() threw; scheduling restart");
|
|
803
|
+
this.scheduleRestart();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
scheduleRestart() {
|
|
807
|
+
if (this.destroyed || this.intentionallyStopped || !this.restartAllowed || this.ttsSpeaking)
|
|
808
|
+
return;
|
|
809
|
+
if (this.engineActive) {
|
|
810
|
+
this.log("scheduleRestart: engine active, not scheduling");
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
const delay = Math.min(this.backoffMs, 2e3);
|
|
814
|
+
this.log(`scheduleRestart in ${delay}ms`);
|
|
815
|
+
if (this.restartTimer) {
|
|
816
|
+
this.log("scheduleRestart: restart already scheduled");
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
this.restartTimer = setTimeout(() => {
|
|
820
|
+
this.restartTimer = null;
|
|
821
|
+
if (this.destroyed || this.intentionallyStopped || !this.restartAllowed || this.ttsSpeaking)
|
|
822
|
+
return;
|
|
823
|
+
this.safeRestart();
|
|
824
|
+
}, delay);
|
|
825
|
+
this.backoffMs = Math.min(this.backoffMs * 2, 2e3);
|
|
826
|
+
}
|
|
827
|
+
prewarmAudio() {
|
|
828
|
+
return __awaiter3(this, void 0, void 0, function* () {
|
|
829
|
+
if (this.prewarmed)
|
|
830
|
+
return;
|
|
831
|
+
try {
|
|
832
|
+
if (typeof navigator === "undefined" || !("mediaDevices" in navigator))
|
|
833
|
+
return;
|
|
834
|
+
const md = navigator.mediaDevices;
|
|
835
|
+
if (!(md === null || md === void 0 ? void 0 : md.getUserMedia))
|
|
836
|
+
return;
|
|
837
|
+
this.log("prewarmAudio: requesting mic");
|
|
838
|
+
const stream = yield md.getUserMedia({ audio: true });
|
|
839
|
+
for (const track of stream.getTracks())
|
|
840
|
+
track.stop();
|
|
841
|
+
this.prewarmed = true;
|
|
842
|
+
this.log("prewarmAudio: mic ready");
|
|
843
|
+
} catch (_a) {
|
|
844
|
+
this.warn("prewarmAudio: failed to get mic");
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
emitStatus(status, details) {
|
|
849
|
+
if (!this.statusCallback)
|
|
850
|
+
return;
|
|
851
|
+
try {
|
|
852
|
+
this.statusCallback(status, details);
|
|
853
|
+
} catch (_a) {
|
|
854
|
+
this.error("VoiceProcessor: status callback error");
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
// dist/libs/core/src/lib/utils/dom-interactions.js
|
|
860
|
+
var GestureHandler = class {
|
|
861
|
+
constructor() {
|
|
862
|
+
this.lastTap = 0;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Sets up double-click and double-tap listeners
|
|
866
|
+
* @param onDoubleClickOrTap Callback to execute when a double-click or double-tap is detected
|
|
867
|
+
*/
|
|
868
|
+
setupDoubleTapListener(onDoubleClickOrTap) {
|
|
869
|
+
this.destroy();
|
|
870
|
+
this.dblClickListener = () => {
|
|
871
|
+
onDoubleClickOrTap();
|
|
872
|
+
};
|
|
873
|
+
document.addEventListener("dblclick", this.dblClickListener);
|
|
874
|
+
this.touchEndListener = () => {
|
|
875
|
+
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
876
|
+
const tapInterval = currentTime - this.lastTap;
|
|
877
|
+
if (tapInterval < 300 && tapInterval > 0) {
|
|
878
|
+
onDoubleClickOrTap();
|
|
879
|
+
}
|
|
880
|
+
this.lastTap = currentTime;
|
|
881
|
+
};
|
|
882
|
+
document.addEventListener("touchend", this.touchEndListener);
|
|
883
|
+
}
|
|
884
|
+
destroy() {
|
|
885
|
+
if (this.dblClickListener) {
|
|
886
|
+
document.removeEventListener("dblclick", this.dblClickListener);
|
|
887
|
+
}
|
|
888
|
+
if (this.touchEndListener) {
|
|
889
|
+
document.removeEventListener("touchend", this.touchEndListener);
|
|
890
|
+
}
|
|
891
|
+
this.dblClickListener = void 0;
|
|
892
|
+
this.touchEndListener = void 0;
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
function injectStyles() {
|
|
896
|
+
const existingStyle = document.querySelector("#assistant-styles");
|
|
897
|
+
if (existingStyle) {
|
|
898
|
+
console.log("Styles already injected");
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
const style = document.createElement("style");
|
|
902
|
+
style.id = "assistant-styles";
|
|
903
|
+
style.innerHTML = `
|
|
904
|
+
/* Rounded shape with gradient animation */
|
|
905
|
+
.gradient-indicator {
|
|
906
|
+
position: fixed;
|
|
907
|
+
top: 20px;
|
|
908
|
+
right: 20px;
|
|
909
|
+
width: 60px;
|
|
910
|
+
height: 60px;
|
|
911
|
+
border-radius: 50%;
|
|
912
|
+
background: linear-gradient(135deg, #ff6ec4, #7873f5, #5e8cff, #6ed0f6);
|
|
913
|
+
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
|
|
914
|
+
animation: amoeba 5s infinite ease-in-out;
|
|
915
|
+
z-index: 9999; /* Ensure it's above all other elements */
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* Amoeba effect for the borders */
|
|
919
|
+
@keyframes amoeba {
|
|
920
|
+
0% {
|
|
921
|
+
border-radius: 50%;
|
|
922
|
+
}
|
|
923
|
+
25% {
|
|
924
|
+
border-radius: 40% 60% 60% 40%;
|
|
925
|
+
}
|
|
926
|
+
50% {
|
|
927
|
+
border-radius: 60% 40% 40% 60%;
|
|
928
|
+
}
|
|
929
|
+
75% {
|
|
930
|
+
border-radius: 40% 60% 60% 40%;
|
|
931
|
+
}
|
|
932
|
+
100% {
|
|
933
|
+
border-radius: 50%;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
`;
|
|
937
|
+
document.head.appendChild(style);
|
|
938
|
+
console.log("Gradient styles injected");
|
|
939
|
+
}
|
|
940
|
+
function addGradientAnimation() {
|
|
941
|
+
if (document.querySelector("#gradient-indicator")) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
const gradientDiv = document.createElement("div");
|
|
945
|
+
gradientDiv.id = "gradient-indicator";
|
|
946
|
+
injectStyles();
|
|
947
|
+
gradientDiv.classList.add("gradient-indicator");
|
|
948
|
+
document.body.appendChild(gradientDiv);
|
|
949
|
+
console.log("Gradient indicator added to the DOM");
|
|
950
|
+
}
|
|
951
|
+
function removeGradientAnimation() {
|
|
952
|
+
const gradientDiv = document.querySelector("#gradient-indicator");
|
|
953
|
+
if (gradientDiv) {
|
|
954
|
+
gradientDiv.remove();
|
|
955
|
+
console.log("Gradient indicator removed from the DOM");
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// dist/libs/core/src/lib/utils/is-browser.js
|
|
960
|
+
function isBrowser() {
|
|
961
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// dist/libs/core/src/lib/state-manager.js
|
|
965
|
+
var StateManager = class {
|
|
966
|
+
constructor() {
|
|
967
|
+
this.state = "idle";
|
|
968
|
+
this.subscribers = [];
|
|
969
|
+
}
|
|
970
|
+
getState() {
|
|
971
|
+
return this.state;
|
|
972
|
+
}
|
|
973
|
+
setState(state) {
|
|
974
|
+
this.state = state;
|
|
975
|
+
this.notifySubscribers();
|
|
976
|
+
console.log("State updated:", state);
|
|
977
|
+
if (state === "listening") {
|
|
978
|
+
addGradientAnimation();
|
|
979
|
+
} else {
|
|
980
|
+
removeGradientAnimation();
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
// eslint-disable-next-line no-unused-vars
|
|
984
|
+
subscribe(callback) {
|
|
985
|
+
this.subscribers.push(callback);
|
|
986
|
+
}
|
|
987
|
+
notifySubscribers() {
|
|
988
|
+
this.subscribers.forEach((callback) => callback(this.state));
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
// dist/libs/core/src/lib/ui/overlay-manager.js
|
|
993
|
+
import { __awaiter as __awaiter4 } from "tslib";
|
|
994
|
+
var OverlayManager = class {
|
|
995
|
+
constructor(config) {
|
|
996
|
+
this.container = null;
|
|
997
|
+
this.chatWindow = null;
|
|
998
|
+
this.messagesContainer = null;
|
|
999
|
+
this.input = null;
|
|
1000
|
+
this.isOpen = false;
|
|
1001
|
+
this.loadingEl = null;
|
|
1002
|
+
this.active = isBrowser();
|
|
1003
|
+
this.config = config;
|
|
1004
|
+
if (this.active)
|
|
1005
|
+
this.init();
|
|
1006
|
+
}
|
|
1007
|
+
init() {
|
|
1008
|
+
var _a, _b;
|
|
1009
|
+
if (this.container)
|
|
1010
|
+
return;
|
|
1011
|
+
this.injectOverlayStyles();
|
|
1012
|
+
const existing = document.getElementById("foisit-overlay-container");
|
|
1013
|
+
if (existing && existing instanceof HTMLElement) {
|
|
1014
|
+
this.container = existing;
|
|
1015
|
+
this.chatWindow = existing.querySelector(".foisit-chat");
|
|
1016
|
+
this.messagesContainer = existing.querySelector(".foisit-messages");
|
|
1017
|
+
this.input = existing.querySelector("input.foisit-input");
|
|
1018
|
+
if (((_a = this.config.floatingButton) === null || _a === void 0 ? void 0 : _a.visible) !== false && !existing.querySelector(".foisit-floating-btn")) {
|
|
1019
|
+
this.renderFloatingButton();
|
|
1020
|
+
}
|
|
1021
|
+
if (!this.chatWindow) {
|
|
1022
|
+
this.renderChatWindow();
|
|
1023
|
+
}
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
this.container = document.createElement("div");
|
|
1027
|
+
this.container.id = "foisit-overlay-container";
|
|
1028
|
+
this.container.className = "foisit-overlay-container";
|
|
1029
|
+
document.body.appendChild(this.container);
|
|
1030
|
+
if (((_b = this.config.floatingButton) === null || _b === void 0 ? void 0 : _b.visible) !== false) {
|
|
1031
|
+
this.renderFloatingButton();
|
|
1032
|
+
}
|
|
1033
|
+
this.renderChatWindow();
|
|
1034
|
+
}
|
|
1035
|
+
renderFloatingButton() {
|
|
1036
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1037
|
+
const btn = document.createElement("button");
|
|
1038
|
+
btn.innerHTML = ((_a = this.config.floatingButton) === null || _a === void 0 ? void 0 : _a.customHtml) || "\u{1F399}\uFE0F";
|
|
1039
|
+
const bottom = ((_c = (_b = this.config.floatingButton) === null || _b === void 0 ? void 0 : _b.position) === null || _c === void 0 ? void 0 : _c.bottom) || "20px";
|
|
1040
|
+
const right = ((_e = (_d = this.config.floatingButton) === null || _d === void 0 ? void 0 : _d.position) === null || _e === void 0 ? void 0 : _e.right) || "20px";
|
|
1041
|
+
btn.className = "foisit-floating-btn";
|
|
1042
|
+
btn.style.bottom = bottom;
|
|
1043
|
+
btn.style.right = right;
|
|
1044
|
+
btn.onclick = () => this.toggle();
|
|
1045
|
+
btn.onmouseenter = () => btn.style.transform = "scale(1.05)";
|
|
1046
|
+
btn.onmouseleave = () => btn.style.transform = "scale(1)";
|
|
1047
|
+
(_f = this.container) === null || _f === void 0 ? void 0 : _f.appendChild(btn);
|
|
1048
|
+
}
|
|
1049
|
+
renderChatWindow() {
|
|
1050
|
+
var _a;
|
|
1051
|
+
if (this.chatWindow)
|
|
1052
|
+
return;
|
|
1053
|
+
this.chatWindow = document.createElement("div");
|
|
1054
|
+
this.chatWindow.className = "foisit-chat";
|
|
1055
|
+
const header = document.createElement("div");
|
|
1056
|
+
header.className = "foisit-header";
|
|
1057
|
+
const title = document.createElement("span");
|
|
1058
|
+
title.className = "foisit-title";
|
|
1059
|
+
title.textContent = "Foisit";
|
|
1060
|
+
const closeButton = document.createElement("button");
|
|
1061
|
+
closeButton.type = "button";
|
|
1062
|
+
closeButton.className = "foisit-close";
|
|
1063
|
+
closeButton.setAttribute("aria-label", "Close");
|
|
1064
|
+
closeButton.innerHTML = "×";
|
|
1065
|
+
closeButton.addEventListener("click", () => this.toggle());
|
|
1066
|
+
header.appendChild(title);
|
|
1067
|
+
header.appendChild(closeButton);
|
|
1068
|
+
this.messagesContainer = document.createElement("div");
|
|
1069
|
+
this.messagesContainer.className = "foisit-messages";
|
|
1070
|
+
const inputArea = document.createElement("div");
|
|
1071
|
+
inputArea.className = "foisit-input-area";
|
|
1072
|
+
this.input = document.createElement("input");
|
|
1073
|
+
this.input.placeholder = this.config.inputPlaceholder || "Type a command...";
|
|
1074
|
+
this.input.className = "foisit-input";
|
|
1075
|
+
this.input.addEventListener("keydown", (e) => {
|
|
1076
|
+
var _a2;
|
|
1077
|
+
if (e.key === "Enter" && ((_a2 = this.input) === null || _a2 === void 0 ? void 0 : _a2.value.trim())) {
|
|
1078
|
+
const text = this.input.value.trim();
|
|
1079
|
+
this.input.value = "";
|
|
1080
|
+
if (this.onSubmit)
|
|
1081
|
+
this.onSubmit(text);
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
inputArea.appendChild(this.input);
|
|
1085
|
+
this.chatWindow.appendChild(header);
|
|
1086
|
+
this.chatWindow.appendChild(this.messagesContainer);
|
|
1087
|
+
this.chatWindow.appendChild(inputArea);
|
|
1088
|
+
(_a = this.container) === null || _a === void 0 ? void 0 : _a.appendChild(this.chatWindow);
|
|
1089
|
+
}
|
|
1090
|
+
registerCallbacks(onSubmit, onClose) {
|
|
1091
|
+
if (!this.active)
|
|
1092
|
+
return;
|
|
1093
|
+
this.onSubmit = onSubmit;
|
|
1094
|
+
this.onClose = onClose;
|
|
1095
|
+
}
|
|
1096
|
+
toggle(onSubmit, onClose) {
|
|
1097
|
+
if (!this.active)
|
|
1098
|
+
return;
|
|
1099
|
+
if (onSubmit)
|
|
1100
|
+
this.onSubmit = onSubmit;
|
|
1101
|
+
if (onClose)
|
|
1102
|
+
this.onClose = onClose;
|
|
1103
|
+
this.isOpen = !this.isOpen;
|
|
1104
|
+
if (this.chatWindow) {
|
|
1105
|
+
if (this.isOpen) {
|
|
1106
|
+
this.chatWindow.style.display = "flex";
|
|
1107
|
+
requestAnimationFrame(() => {
|
|
1108
|
+
if (this.chatWindow) {
|
|
1109
|
+
this.chatWindow.style.opacity = "1";
|
|
1110
|
+
this.chatWindow.style.transform = "translateY(0) scale(1)";
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
setTimeout(() => {
|
|
1114
|
+
var _a;
|
|
1115
|
+
return (_a = this.input) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1116
|
+
}, 100);
|
|
1117
|
+
} else {
|
|
1118
|
+
this.chatWindow.style.opacity = "0";
|
|
1119
|
+
this.chatWindow.style.transform = "translateY(20px) scale(0.95)";
|
|
1120
|
+
setTimeout(() => {
|
|
1121
|
+
if (this.chatWindow && !this.isOpen) {
|
|
1122
|
+
this.chatWindow.style.display = "none";
|
|
1123
|
+
}
|
|
1124
|
+
}, 200);
|
|
1125
|
+
if (this.onClose)
|
|
1126
|
+
this.onClose();
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
addMessage(text, type) {
|
|
1131
|
+
if (!this.messagesContainer)
|
|
1132
|
+
return;
|
|
1133
|
+
const msg = document.createElement("div");
|
|
1134
|
+
msg.textContent = text;
|
|
1135
|
+
msg.className = type === "user" ? "foisit-bubble user" : "foisit-bubble system";
|
|
1136
|
+
this.messagesContainer.appendChild(msg);
|
|
1137
|
+
this.scrollToBottom();
|
|
1138
|
+
}
|
|
1139
|
+
addOptions(options) {
|
|
1140
|
+
if (!this.messagesContainer)
|
|
1141
|
+
return;
|
|
1142
|
+
const container = document.createElement("div");
|
|
1143
|
+
container.className = "foisit-options-container";
|
|
1144
|
+
options.forEach((opt) => {
|
|
1145
|
+
const btn = document.createElement("button");
|
|
1146
|
+
btn.textContent = opt.label;
|
|
1147
|
+
btn.className = "foisit-option-chip";
|
|
1148
|
+
btn.setAttribute("type", "button");
|
|
1149
|
+
btn.setAttribute("aria-label", opt.label);
|
|
1150
|
+
const clickPayload = () => {
|
|
1151
|
+
if (opt.commandId) {
|
|
1152
|
+
if (this.onSubmit)
|
|
1153
|
+
this.onSubmit({ commandId: opt.commandId });
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const value = opt && typeof opt.value === "string" && opt.value.trim() ? opt.value : opt.label;
|
|
1157
|
+
if (this.onSubmit)
|
|
1158
|
+
this.onSubmit(value);
|
|
1159
|
+
};
|
|
1160
|
+
btn.onclick = clickPayload;
|
|
1161
|
+
btn.onkeydown = (e) => {
|
|
1162
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1163
|
+
e.preventDefault();
|
|
1164
|
+
clickPayload();
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
container.appendChild(btn);
|
|
1168
|
+
});
|
|
1169
|
+
this.messagesContainer.appendChild(container);
|
|
1170
|
+
this.scrollToBottom();
|
|
1171
|
+
}
|
|
1172
|
+
addForm(message, fields, onSubmit) {
|
|
1173
|
+
if (!this.messagesContainer)
|
|
1174
|
+
return;
|
|
1175
|
+
this.addMessage(message, "system");
|
|
1176
|
+
const form = document.createElement("form");
|
|
1177
|
+
form.className = "foisit-form";
|
|
1178
|
+
const controls = [];
|
|
1179
|
+
const createLabel = (text, required) => {
|
|
1180
|
+
const label = document.createElement("div");
|
|
1181
|
+
label.className = "foisit-form-label";
|
|
1182
|
+
label.innerHTML = text + (required ? ' <span class="foisit-req-star">*</span>' : "");
|
|
1183
|
+
return label;
|
|
1184
|
+
};
|
|
1185
|
+
const createError = () => {
|
|
1186
|
+
const error = document.createElement("div");
|
|
1187
|
+
error.className = "foisit-form-error";
|
|
1188
|
+
error.style.display = "none";
|
|
1189
|
+
return error;
|
|
1190
|
+
};
|
|
1191
|
+
(fields !== null && fields !== void 0 ? fields : []).forEach((field) => {
|
|
1192
|
+
const wrapper = document.createElement("div");
|
|
1193
|
+
wrapper.className = "foisit-form-group";
|
|
1194
|
+
const labelText = field.description || field.name;
|
|
1195
|
+
wrapper.appendChild(createLabel(labelText, field.required));
|
|
1196
|
+
let inputEl;
|
|
1197
|
+
if (field.type === "select") {
|
|
1198
|
+
const select = document.createElement("select");
|
|
1199
|
+
select.className = "foisit-form-input";
|
|
1200
|
+
const placeholderOpt = document.createElement("option");
|
|
1201
|
+
placeholderOpt.value = "";
|
|
1202
|
+
placeholderOpt.textContent = "Select...";
|
|
1203
|
+
select.appendChild(placeholderOpt);
|
|
1204
|
+
const populate = (options) => {
|
|
1205
|
+
(options !== null && options !== void 0 ? options : []).forEach((opt) => {
|
|
1206
|
+
var _a, _b, _c, _d;
|
|
1207
|
+
const o = document.createElement("option");
|
|
1208
|
+
o.value = String((_b = (_a = opt.value) !== null && _a !== void 0 ? _a : opt.label) !== null && _b !== void 0 ? _b : "");
|
|
1209
|
+
o.textContent = String((_d = (_c = opt.label) !== null && _c !== void 0 ? _c : opt.value) !== null && _d !== void 0 ? _d : "");
|
|
1210
|
+
select.appendChild(o);
|
|
1211
|
+
});
|
|
1212
|
+
};
|
|
1213
|
+
if (Array.isArray(field.options) && field.options.length) {
|
|
1214
|
+
populate(field.options);
|
|
1215
|
+
} else if (typeof field.getOptions === "function") {
|
|
1216
|
+
const getOptions = field.getOptions;
|
|
1217
|
+
const loadingOpt = document.createElement("option");
|
|
1218
|
+
loadingOpt.value = "";
|
|
1219
|
+
loadingOpt.textContent = "Loading...";
|
|
1220
|
+
select.appendChild(loadingOpt);
|
|
1221
|
+
Promise.resolve().then(() => getOptions()).then((opts) => {
|
|
1222
|
+
while (select.options.length > 1)
|
|
1223
|
+
select.remove(1);
|
|
1224
|
+
populate(opts);
|
|
1225
|
+
}).catch(() => {
|
|
1226
|
+
while (select.options.length > 1)
|
|
1227
|
+
select.remove(1);
|
|
1228
|
+
const errOpt = document.createElement("option");
|
|
1229
|
+
errOpt.value = "";
|
|
1230
|
+
errOpt.textContent = "Error loading options";
|
|
1231
|
+
select.appendChild(errOpt);
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
if (field.defaultValue != null) {
|
|
1235
|
+
select.value = String(field.defaultValue);
|
|
1236
|
+
}
|
|
1237
|
+
inputEl = select;
|
|
1238
|
+
} else if (field.type === "file") {
|
|
1239
|
+
const ffield = field;
|
|
1240
|
+
const input = document.createElement("input");
|
|
1241
|
+
input.className = "foisit-form-input";
|
|
1242
|
+
input.type = "file";
|
|
1243
|
+
if (ffield.accept && Array.isArray(ffield.accept)) {
|
|
1244
|
+
input.accept = ffield.accept.join(",");
|
|
1245
|
+
}
|
|
1246
|
+
if (ffield.multiple)
|
|
1247
|
+
input.multiple = true;
|
|
1248
|
+
if (ffield.capture) {
|
|
1249
|
+
if (ffield.capture === true)
|
|
1250
|
+
input.setAttribute("capture", "");
|
|
1251
|
+
else
|
|
1252
|
+
input.setAttribute("capture", String(ffield.capture));
|
|
1253
|
+
}
|
|
1254
|
+
input.addEventListener("change", () => __awaiter4(this, void 0, void 0, function* () {
|
|
1255
|
+
var _a, _b, _c;
|
|
1256
|
+
const files = Array.from(input.files || []);
|
|
1257
|
+
const errEl = errorEl;
|
|
1258
|
+
errEl.style.display = "none";
|
|
1259
|
+
errEl.textContent = "";
|
|
1260
|
+
if (files.length === 0)
|
|
1261
|
+
return;
|
|
1262
|
+
const maxFiles = (_a = ffield.maxFiles) !== null && _a !== void 0 ? _a : ffield.multiple ? 10 : 1;
|
|
1263
|
+
if (files.length > maxFiles) {
|
|
1264
|
+
errEl.textContent = `Please select at most ${maxFiles} file(s).`;
|
|
1265
|
+
errEl.style.display = "block";
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const maxSize = (_b = ffield.maxSizeBytes) !== null && _b !== void 0 ? _b : Infinity;
|
|
1269
|
+
const total = files.reduce((s, f) => s + f.size, 0);
|
|
1270
|
+
if (files.some((f) => f.size > maxSize)) {
|
|
1271
|
+
errEl.textContent = `One or more files exceed the maximum size of ${Math.round(maxSize / 1024)} KB.`;
|
|
1272
|
+
errEl.style.display = "block";
|
|
190
1273
|
return;
|
|
1274
|
+
}
|
|
1275
|
+
const maxTotal = (_c = ffield.maxTotalBytes) !== null && _c !== void 0 ? _c : Infinity;
|
|
1276
|
+
if (total > maxTotal) {
|
|
1277
|
+
errEl.textContent = `Total selected files exceed the maximum of ${Math.round(maxTotal / 1024)} KB.`;
|
|
1278
|
+
errEl.style.display = "block";
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
if (ffield.accept && Array.isArray(ffield.accept)) {
|
|
1282
|
+
const accepts = ffield.accept;
|
|
1283
|
+
const ok = files.every((file) => {
|
|
1284
|
+
if (!file.type)
|
|
1285
|
+
return true;
|
|
1286
|
+
return accepts.some((a) => a.startsWith(".") ? file.name.toLowerCase().endsWith(a.toLowerCase()) : file.type === a || file.type.startsWith(a.split("/")[0] + "/"));
|
|
1287
|
+
});
|
|
1288
|
+
if (!ok) {
|
|
1289
|
+
errEl.textContent = "One or more files have an unsupported type.";
|
|
1290
|
+
errEl.style.display = "block";
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}));
|
|
1295
|
+
inputEl = input;
|
|
1296
|
+
} else {
|
|
1297
|
+
const input = document.createElement("input");
|
|
1298
|
+
input.className = "foisit-form-input";
|
|
1299
|
+
if (field.type === "string") {
|
|
1300
|
+
input.placeholder = field.placeholder || "Type here...";
|
|
1301
|
+
}
|
|
1302
|
+
if (field.type === "number") {
|
|
1303
|
+
input.type = "number";
|
|
1304
|
+
if (typeof field.min === "number")
|
|
1305
|
+
input.min = String(field.min);
|
|
1306
|
+
if (typeof field.max === "number")
|
|
1307
|
+
input.max = String(field.max);
|
|
1308
|
+
if (typeof field.step === "number")
|
|
1309
|
+
input.step = String(field.step);
|
|
1310
|
+
if (field.defaultValue != null)
|
|
1311
|
+
input.value = String(field.defaultValue);
|
|
1312
|
+
} else if (field.type === "date") {
|
|
1313
|
+
input.type = "date";
|
|
1314
|
+
if (typeof field.min === "string")
|
|
1315
|
+
input.min = field.min;
|
|
1316
|
+
if (typeof field.max === "string")
|
|
1317
|
+
input.max = field.max;
|
|
1318
|
+
if (field.defaultValue != null)
|
|
1319
|
+
input.value = String(field.defaultValue);
|
|
1320
|
+
} else {
|
|
1321
|
+
input.type = "text";
|
|
1322
|
+
if (field.defaultValue != null)
|
|
1323
|
+
input.value = String(field.defaultValue);
|
|
191
1324
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
1325
|
+
inputEl = input;
|
|
1326
|
+
}
|
|
1327
|
+
const errorEl = createError();
|
|
1328
|
+
wrapper.appendChild(inputEl);
|
|
1329
|
+
wrapper.appendChild(errorEl);
|
|
1330
|
+
controls.push({
|
|
1331
|
+
name: field.name,
|
|
1332
|
+
type: field.type,
|
|
1333
|
+
el: inputEl,
|
|
1334
|
+
required: field.required
|
|
1335
|
+
});
|
|
1336
|
+
form.appendChild(wrapper);
|
|
1337
|
+
});
|
|
1338
|
+
const actions = document.createElement("div");
|
|
1339
|
+
actions.className = "foisit-form-actions";
|
|
1340
|
+
const submitBtn = document.createElement("button");
|
|
1341
|
+
submitBtn.type = "submit";
|
|
1342
|
+
submitBtn.textContent = "Submit";
|
|
1343
|
+
submitBtn.className = "foisit-option-chip";
|
|
1344
|
+
submitBtn.style.fontWeight = "600";
|
|
1345
|
+
actions.appendChild(submitBtn);
|
|
1346
|
+
form.appendChild(actions);
|
|
1347
|
+
form.onsubmit = (e) => __awaiter4(this, void 0, void 0, function* () {
|
|
1348
|
+
var _a, _b;
|
|
1349
|
+
e.preventDefault();
|
|
1350
|
+
const data = {};
|
|
1351
|
+
let hasError = false;
|
|
1352
|
+
form.querySelectorAll(".foisit-form-error").forEach((el) => {
|
|
1353
|
+
el.style.display = "none";
|
|
1354
|
+
el.textContent = "";
|
|
1355
|
+
});
|
|
1356
|
+
form.querySelectorAll(".foisit-form-input").forEach((el) => {
|
|
1357
|
+
el.classList.remove("foisit-error-border");
|
|
1358
|
+
});
|
|
1359
|
+
for (const c of controls) {
|
|
1360
|
+
if (c.type === "file") {
|
|
1361
|
+
const fileWrapper = c.el.parentElement;
|
|
1362
|
+
const fileErrorEl = fileWrapper === null || fileWrapper === void 0 ? void 0 : fileWrapper.querySelector(".foisit-form-error");
|
|
1363
|
+
const input = c.el;
|
|
1364
|
+
const files = Array.from(input.files || []);
|
|
1365
|
+
if (c.required && files.length === 0) {
|
|
1366
|
+
hasError = true;
|
|
1367
|
+
input.classList.add("foisit-error-border");
|
|
1368
|
+
if (fileErrorEl) {
|
|
1369
|
+
fileErrorEl.textContent = "This file is required";
|
|
1370
|
+
fileErrorEl.style.display = "block";
|
|
1371
|
+
}
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
if (files.length === 0)
|
|
1375
|
+
continue;
|
|
1376
|
+
const fieldDef = (fields !== null && fields !== void 0 ? fields : []).find((f) => f.name === c.name);
|
|
1377
|
+
const delivery = (_a = fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.delivery) !== null && _a !== void 0 ? _a : "file";
|
|
1378
|
+
if ((fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.maxWidth) || (fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.maxHeight)) {
|
|
1379
|
+
try {
|
|
1380
|
+
const dims = yield this.getImageDimensions(files[0]);
|
|
1381
|
+
if (fieldDef.maxWidth && dims.width > fieldDef.maxWidth) {
|
|
1382
|
+
hasError = true;
|
|
1383
|
+
if (fileErrorEl) {
|
|
1384
|
+
fileErrorEl.textContent = `Image width must be \u2264 ${fieldDef.maxWidth}px`;
|
|
1385
|
+
fileErrorEl.style.display = "block";
|
|
198
1386
|
}
|
|
199
|
-
|
|
200
|
-
|
|
1387
|
+
continue;
|
|
1388
|
+
}
|
|
1389
|
+
if (fieldDef.maxHeight && dims.height > fieldDef.maxHeight) {
|
|
1390
|
+
hasError = true;
|
|
1391
|
+
if (fileErrorEl) {
|
|
1392
|
+
fileErrorEl.textContent = `Image height must be \u2264 ${fieldDef.maxHeight}px`;
|
|
1393
|
+
fileErrorEl.style.display = "block";
|
|
201
1394
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
if ((response.type === 'ambiguous' || response.type === 'confirm') && response.options) {
|
|
207
|
-
if (response.message) {
|
|
208
|
-
this.overlayManager.addMessage(response.message, 'system');
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
} catch (_c) {
|
|
209
1398
|
}
|
|
210
|
-
|
|
211
|
-
|
|
1399
|
+
}
|
|
1400
|
+
if (fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.maxDurationSec) {
|
|
1401
|
+
try {
|
|
1402
|
+
const dur = yield this.getMediaDuration(files[0]);
|
|
1403
|
+
if (dur && dur > fieldDef.maxDurationSec) {
|
|
1404
|
+
hasError = true;
|
|
1405
|
+
if (fileErrorEl) {
|
|
1406
|
+
fileErrorEl.textContent = `Media duration must be \u2264 ${fieldDef.maxDurationSec}s`;
|
|
1407
|
+
fileErrorEl.style.display = "block";
|
|
1408
|
+
}
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
} catch (_d) {
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (delivery === "file") {
|
|
1415
|
+
data[c.name] = (fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.multiple) ? files : files[0];
|
|
1416
|
+
} else if (delivery === "base64") {
|
|
1417
|
+
try {
|
|
1418
|
+
const encoded = yield Promise.all(files.map((file) => this.readFileAsDataURL(file)));
|
|
1419
|
+
data[c.name] = (fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.multiple) ? encoded : encoded[0];
|
|
1420
|
+
} catch (_e) {
|
|
1421
|
+
hasError = true;
|
|
1422
|
+
if (fileErrorEl) {
|
|
1423
|
+
fileErrorEl.textContent = "Failed to encode file(s) to base64.";
|
|
1424
|
+
fileErrorEl.style.display = "block";
|
|
1425
|
+
}
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
continue;
|
|
212
1430
|
}
|
|
213
|
-
|
|
214
|
-
|
|
1431
|
+
const val = ((_b = c.el.value) !== null && _b !== void 0 ? _b : "").toString().trim();
|
|
1432
|
+
const valueWrapper = c.el.parentElement;
|
|
1433
|
+
const fieldErrorEl = valueWrapper === null || valueWrapper === void 0 ? void 0 : valueWrapper.querySelector(".foisit-form-error");
|
|
1434
|
+
if (c.required && (val == null || val === "")) {
|
|
1435
|
+
hasError = true;
|
|
1436
|
+
c.el.classList.add("foisit-error-border");
|
|
1437
|
+
if (fieldErrorEl) {
|
|
1438
|
+
fieldErrorEl.textContent = "This field is required";
|
|
1439
|
+
fieldErrorEl.style.display = "block";
|
|
1440
|
+
}
|
|
1441
|
+
continue;
|
|
215
1442
|
}
|
|
1443
|
+
if (val === "")
|
|
1444
|
+
continue;
|
|
1445
|
+
if (c.type === "number") {
|
|
1446
|
+
const n = Number(val);
|
|
1447
|
+
if (!Number.isNaN(n))
|
|
1448
|
+
data[c.name] = n;
|
|
1449
|
+
} else {
|
|
1450
|
+
data[c.name] = val;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (hasError) {
|
|
1454
|
+
form.classList.add("foisit-shake");
|
|
1455
|
+
setTimeout(() => form.classList.remove("foisit-shake"), 400);
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
submitBtn.disabled = true;
|
|
1459
|
+
submitBtn.style.opacity = "0.6";
|
|
1460
|
+
controls.forEach((c) => {
|
|
1461
|
+
c.el.disabled = true;
|
|
1462
|
+
});
|
|
1463
|
+
onSubmit(data);
|
|
1464
|
+
});
|
|
1465
|
+
this.messagesContainer.appendChild(form);
|
|
1466
|
+
this.scrollToBottom();
|
|
1467
|
+
}
|
|
1468
|
+
showLoading() {
|
|
1469
|
+
if (!this.messagesContainer)
|
|
1470
|
+
return;
|
|
1471
|
+
if (this.loadingEl)
|
|
1472
|
+
return;
|
|
1473
|
+
this.loadingEl = document.createElement("div");
|
|
1474
|
+
this.loadingEl.className = "foisit-loading-dots foisit-bubble system";
|
|
1475
|
+
for (let i = 0; i < 3; i++) {
|
|
1476
|
+
const dot = document.createElement("div");
|
|
1477
|
+
dot.className = "foisit-dot";
|
|
1478
|
+
dot.style.animation = `foisitPulse 1.4s infinite ease-in-out ${i * 0.2}s`;
|
|
1479
|
+
this.loadingEl.appendChild(dot);
|
|
1480
|
+
}
|
|
1481
|
+
this.messagesContainer.appendChild(this.loadingEl);
|
|
1482
|
+
this.scrollToBottom();
|
|
1483
|
+
}
|
|
1484
|
+
hideLoading() {
|
|
1485
|
+
var _a;
|
|
1486
|
+
(_a = this.loadingEl) === null || _a === void 0 ? void 0 : _a.remove();
|
|
1487
|
+
this.loadingEl = null;
|
|
1488
|
+
}
|
|
1489
|
+
scrollToBottom() {
|
|
1490
|
+
if (this.messagesContainer) {
|
|
1491
|
+
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
|
|
216
1492
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
1493
|
+
}
|
|
1494
|
+
destroy() {
|
|
1495
|
+
var _a;
|
|
1496
|
+
(_a = this.container) === null || _a === void 0 ? void 0 : _a.remove();
|
|
1497
|
+
this.container = null;
|
|
1498
|
+
this.chatWindow = null;
|
|
1499
|
+
this.messagesContainer = null;
|
|
1500
|
+
this.input = null;
|
|
1501
|
+
this.isOpen = false;
|
|
1502
|
+
}
|
|
1503
|
+
readFileAsDataURL(file) {
|
|
1504
|
+
return new Promise((resolve, reject) => {
|
|
1505
|
+
const fr = new FileReader();
|
|
1506
|
+
fr.onerror = () => reject(new Error("Failed to read file"));
|
|
1507
|
+
fr.onload = () => resolve(String(fr.result));
|
|
1508
|
+
fr.readAsDataURL(file);
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
getImageDimensions(file) {
|
|
1512
|
+
return new Promise((resolve) => {
|
|
1513
|
+
try {
|
|
1514
|
+
const url = URL.createObjectURL(file);
|
|
1515
|
+
const img = new Image();
|
|
1516
|
+
img.onload = () => {
|
|
1517
|
+
const dims = { width: img.naturalWidth || img.width, height: img.naturalHeight || img.height };
|
|
1518
|
+
URL.revokeObjectURL(url);
|
|
1519
|
+
resolve(dims);
|
|
1520
|
+
};
|
|
1521
|
+
img.onerror = () => {
|
|
1522
|
+
URL.revokeObjectURL(url);
|
|
1523
|
+
resolve({ width: 0, height: 0 });
|
|
1524
|
+
};
|
|
1525
|
+
img.src = url;
|
|
1526
|
+
} catch (_a) {
|
|
1527
|
+
resolve({ width: 0, height: 0 });
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
getMediaDuration(file) {
|
|
1532
|
+
return new Promise((resolve) => {
|
|
1533
|
+
try {
|
|
1534
|
+
const url = URL.createObjectURL(file);
|
|
1535
|
+
const el = file.type.startsWith("audio") ? document.createElement("audio") : document.createElement("video");
|
|
1536
|
+
let settled = false;
|
|
1537
|
+
const timeout = setTimeout(() => {
|
|
1538
|
+
if (!settled) {
|
|
1539
|
+
settled = true;
|
|
1540
|
+
URL.revokeObjectURL(url);
|
|
1541
|
+
resolve(0);
|
|
1542
|
+
}
|
|
1543
|
+
}, 5e3);
|
|
1544
|
+
el.preload = "metadata";
|
|
1545
|
+
el.onloadedmetadata = () => {
|
|
1546
|
+
if (settled)
|
|
1547
|
+
return;
|
|
1548
|
+
settled = true;
|
|
1549
|
+
clearTimeout(timeout);
|
|
1550
|
+
const mediaEl = el;
|
|
1551
|
+
const d = mediaEl.duration || 0;
|
|
1552
|
+
URL.revokeObjectURL(url);
|
|
1553
|
+
resolve(d);
|
|
1554
|
+
};
|
|
1555
|
+
el.onerror = () => {
|
|
1556
|
+
if (settled)
|
|
1557
|
+
return;
|
|
1558
|
+
settled = true;
|
|
1559
|
+
clearTimeout(timeout);
|
|
1560
|
+
URL.revokeObjectURL(url);
|
|
1561
|
+
resolve(0);
|
|
1562
|
+
};
|
|
1563
|
+
el.src = url;
|
|
1564
|
+
} catch (_a) {
|
|
1565
|
+
resolve(0);
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
injectOverlayStyles() {
|
|
1570
|
+
if (document.getElementById("foisit-overlay-styles"))
|
|
1571
|
+
return;
|
|
1572
|
+
const style = document.createElement("style");
|
|
1573
|
+
style.id = "foisit-overlay-styles";
|
|
1574
|
+
style.textContent = `
|
|
1575
|
+
:root {
|
|
1576
|
+
/* LIGHT MODE (Default) - Smoother gradient */
|
|
1577
|
+
/* Changed: Softer, right-focused radial highlight to avoid a heavy white bottom */
|
|
1578
|
+
--foisit-bg: radial-gradient(ellipse at 75% 30%, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.03));
|
|
1579
|
+
--foisit-border: 1px solid rgba(255, 255, 255, 0.25);
|
|
1580
|
+
--foisit-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
1581
|
+
--foisit-text: #333;
|
|
1582
|
+
|
|
1583
|
+
/* Input */
|
|
1584
|
+
--foisit-input-color: #333;
|
|
1585
|
+
--foisit-input-placeholder: rgba(60, 60, 67, 0.6);
|
|
1586
|
+
|
|
1587
|
+
/* Bubbles */
|
|
1588
|
+
--foisit-bubble-user-bg: rgba(0, 0, 0, 0.04);
|
|
1589
|
+
--foisit-bubble-user-text: #333;
|
|
1590
|
+
|
|
1591
|
+
--foisit-bubble-sys-bg: rgba(255, 255, 255, 0.45);
|
|
1592
|
+
--foisit-bubble-sys-text: #333;
|
|
1593
|
+
|
|
1594
|
+
/* Form Colors */
|
|
1595
|
+
--foisit-req-star: #ef4444; /* Red asterisk */
|
|
1596
|
+
--foisit-error-text: #dc2626;
|
|
1597
|
+
--foisit-error-border: #fca5a5;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
@media (prefers-color-scheme: dark) {
|
|
1601
|
+
:root {
|
|
1602
|
+
/* DARK MODE */
|
|
1603
|
+
--foisit-bg: linear-gradient(135deg, rgba(40, 40, 40, 0.65), rgba(40, 40, 40, 0.25));
|
|
1604
|
+
--foisit-border: 1px solid rgba(255, 255, 255, 0.1);
|
|
1605
|
+
--foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
|
|
1606
|
+
--foisit-text: #fff;
|
|
1607
|
+
|
|
1608
|
+
/* Input */
|
|
1609
|
+
--foisit-input-color: white;
|
|
1610
|
+
--foisit-input-placeholder: rgba(235, 235, 245, 0.5);
|
|
1611
|
+
|
|
1612
|
+
/* Bubbles */
|
|
1613
|
+
--foisit-bubble-user-bg: rgba(255, 255, 255, 0.1);
|
|
1614
|
+
--foisit-bubble-user-text: white;
|
|
1615
|
+
|
|
1616
|
+
--foisit-bubble-sys-bg: rgba(255, 255, 255, 0.05);
|
|
1617
|
+
--foisit-bubble-sys-text: rgba(255, 255, 255, 0.9);
|
|
1618
|
+
|
|
1619
|
+
/* Form Colors */
|
|
1620
|
+
--foisit-req-star: #f87171;
|
|
1621
|
+
--foisit-error-text: #fca5a5;
|
|
1622
|
+
--foisit-error-border: #f87171;
|
|
221
1623
|
}
|
|
222
|
-
|
|
223
|
-
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
@keyframes foisitPulse {
|
|
1627
|
+
0%, 100% { transform: scale(0.8); opacity: 0.5; }
|
|
1628
|
+
50% { transform: scale(1.2); opacity: 1; }
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
@keyframes foisitShake {
|
|
1632
|
+
0%, 100% { transform: translateX(0); }
|
|
1633
|
+
25% { transform: translateX(-4px); }
|
|
1634
|
+
75% { transform: translateX(4px); }
|
|
1635
|
+
}
|
|
1636
|
+
.foisit-shake { animation: foisitShake 0.4s ease-in-out; }
|
|
1637
|
+
|
|
1638
|
+
/* Container */
|
|
1639
|
+
.foisit-overlay-container {
|
|
1640
|
+
position: fixed;
|
|
1641
|
+
inset: 0;
|
|
1642
|
+
z-index: 2147483647;
|
|
1643
|
+
pointer-events: none;
|
|
1644
|
+
display: flex;
|
|
1645
|
+
flex-direction: column;
|
|
1646
|
+
justify-content: flex-end;
|
|
1647
|
+
align-items: flex-end;
|
|
1648
|
+
padding: 20px;
|
|
1649
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
.foisit-overlay-container * {
|
|
1653
|
+
box-sizing: border-box;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/* Chat Window - Dynamic Height */
|
|
1657
|
+
.foisit-chat {
|
|
1658
|
+
position: absolute;
|
|
1659
|
+
top: 20px;
|
|
1660
|
+
right: 20px;
|
|
1661
|
+
width: min(420px, 92vw);
|
|
1662
|
+
|
|
1663
|
+
/* FIX: Auto height to prevent empty space */
|
|
1664
|
+
height: auto;
|
|
1665
|
+
min-height: 120px;
|
|
1666
|
+
max-height: 80vh;
|
|
1667
|
+
|
|
1668
|
+
background: var(--foisit-bg);
|
|
1669
|
+
border: var(--foisit-border);
|
|
1670
|
+
box-shadow: var(--foisit-shadow);
|
|
1671
|
+
|
|
1672
|
+
backdrop-filter: blur(20px);
|
|
1673
|
+
-webkit-backdrop-filter: blur(20px);
|
|
1674
|
+
|
|
1675
|
+
border-radius: 18px;
|
|
1676
|
+
display: none;
|
|
1677
|
+
flex-direction: column;
|
|
1678
|
+
overflow: hidden;
|
|
1679
|
+
pointer-events: auto;
|
|
1680
|
+
transform-origin: top right;
|
|
1681
|
+
transition: opacity 0.2s, transform 0.2s cubic-bezier(0.2, 0.9, 0.2, 1);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.foisit-header {
|
|
1685
|
+
display: flex;
|
|
1686
|
+
align-items: center;
|
|
1687
|
+
justify-content: space-between;
|
|
1688
|
+
padding: 12px 16px;
|
|
1689
|
+
font-weight: 600;
|
|
1690
|
+
font-size: 14px;
|
|
1691
|
+
color: var(--foisit-text);
|
|
1692
|
+
border-bottom: 1px solid rgba(127,127,127,0.08); /* Subtle separator */
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
.foisit-close {
|
|
1696
|
+
background: transparent;
|
|
1697
|
+
border: none;
|
|
1698
|
+
color: var(--foisit-text);
|
|
1699
|
+
opacity: 0.5;
|
|
1700
|
+
font-size: 24px;
|
|
1701
|
+
line-height: 1;
|
|
1702
|
+
cursor: pointer;
|
|
1703
|
+
padding: 0;
|
|
1704
|
+
width: 28px;
|
|
1705
|
+
height: 28px;
|
|
1706
|
+
display: flex;
|
|
1707
|
+
align-items: center;
|
|
1708
|
+
justify-content: center;
|
|
1709
|
+
transition: opacity 0.2s;
|
|
1710
|
+
}
|
|
1711
|
+
.foisit-close:hover { opacity: 1; }
|
|
1712
|
+
|
|
1713
|
+
/* Message Area */
|
|
1714
|
+
.foisit-messages {
|
|
1715
|
+
flex: 1;
|
|
1716
|
+
overflow-y: auto;
|
|
1717
|
+
padding: 16px;
|
|
1718
|
+
display: flex;
|
|
1719
|
+
flex-direction: column;
|
|
1720
|
+
gap: 10px;
|
|
1721
|
+
/* Ensure it doesn't get too tall initially */
|
|
1722
|
+
min-height: 60px;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/* Make sure empty state isn't huge */
|
|
1726
|
+
.foisit-messages:empty {
|
|
1727
|
+
display: none;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/* Only show messages container if it has children */
|
|
1731
|
+
.foisit-messages:not(:empty) {
|
|
1732
|
+
display: flex;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/* Bubbles */
|
|
1736
|
+
.foisit-bubble {
|
|
1737
|
+
max-width: 90%;
|
|
1738
|
+
padding: 8px 14px;
|
|
1739
|
+
border-radius: 14px;
|
|
1740
|
+
font-size: 14px;
|
|
1741
|
+
line-height: 1.4;
|
|
1742
|
+
word-wrap: break-word;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
.foisit-bubble.user {
|
|
1746
|
+
align-self: flex-end;
|
|
1747
|
+
background: var(--foisit-bubble-user-bg);
|
|
1748
|
+
color: var(--foisit-bubble-user-text);
|
|
1749
|
+
border-bottom-right-radius: 4px;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
.foisit-bubble.system {
|
|
1753
|
+
align-self: flex-start;
|
|
1754
|
+
background: var(--foisit-bubble-sys-bg);
|
|
1755
|
+
color: var(--foisit-bubble-sys-text);
|
|
1756
|
+
border-bottom-left-radius: 4px;
|
|
1757
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/* Input Area */
|
|
1761
|
+
.foisit-input-area {
|
|
1762
|
+
padding: 0;
|
|
1763
|
+
width: 100%;
|
|
1764
|
+
border-top: 1px solid rgba(127,127,127,0.08);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
.foisit-input {
|
|
1768
|
+
width: 100%;
|
|
1769
|
+
background: transparent;
|
|
1770
|
+
border: none;
|
|
1771
|
+
font-size: 16px;
|
|
1772
|
+
color: var(--foisit-input-color);
|
|
1773
|
+
padding: 16px 20px;
|
|
1774
|
+
outline: none;
|
|
1775
|
+
text-align: left;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
.foisit-input::placeholder {
|
|
1779
|
+
color: var(--foisit-input-placeholder);
|
|
1780
|
+
font-weight: 400;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
/* Options & Buttons */
|
|
1784
|
+
.foisit-options-container {
|
|
1785
|
+
display: flex;
|
|
1786
|
+
flex-wrap: wrap;
|
|
1787
|
+
gap: 8px;
|
|
1788
|
+
margin-left: 2px;
|
|
1789
|
+
margin-top: 4px;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
.foisit-option-chip {
|
|
1793
|
+
padding: 6px 14px;
|
|
1794
|
+
background: var(--foisit-bubble-sys-bg);
|
|
1795
|
+
border: 1px solid rgba(127,127,127,0.1);
|
|
1796
|
+
border-radius: 20px;
|
|
1797
|
+
font-size: 13px;
|
|
1798
|
+
color: var(--foisit-text);
|
|
1799
|
+
cursor: pointer;
|
|
1800
|
+
transition: all 0.2s;
|
|
1801
|
+
font-weight: 500;
|
|
1802
|
+
}
|
|
1803
|
+
.foisit-option-chip:hover {
|
|
1804
|
+
background: rgba(127,127,127,0.15);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/* Form Styling */
|
|
1808
|
+
.foisit-form {
|
|
1809
|
+
background: var(--foisit-bubble-sys-bg);
|
|
1810
|
+
padding: 16px;
|
|
1811
|
+
border-radius: 14px;
|
|
1812
|
+
display: flex;
|
|
1813
|
+
flex-direction: column;
|
|
1814
|
+
gap: 12px;
|
|
1815
|
+
width: 100%;
|
|
1816
|
+
border: 1px solid rgba(127,127,127,0.1);
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
.foisit-form-label {
|
|
1820
|
+
font-size: 12px;
|
|
1821
|
+
font-weight: 600;
|
|
1822
|
+
color: var(--foisit-text);
|
|
1823
|
+
opacity: 0.9;
|
|
1824
|
+
margin-bottom: 2px;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
.foisit-req-star {
|
|
1828
|
+
color: var(--foisit-req-star);
|
|
1829
|
+
font-weight: bold;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
.foisit-form-input {
|
|
1833
|
+
width: 100%;
|
|
1834
|
+
padding: 10px;
|
|
1835
|
+
border-radius: 8px;
|
|
1836
|
+
border: 1px solid rgba(127,127,127,0.2);
|
|
1837
|
+
background: rgba(255,255,255,0.05); /* Very subtle fill */
|
|
1838
|
+
color: var(--foisit-text);
|
|
1839
|
+
font-size: 14px;
|
|
1840
|
+
outline: none;
|
|
1841
|
+
transition: border 0.2s;
|
|
1842
|
+
}
|
|
1843
|
+
.foisit-form-input:focus {
|
|
1844
|
+
border-color: var(--foisit-text);
|
|
1845
|
+
background: rgba(255,255,255,0.1);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
.foisit-error-border {
|
|
1849
|
+
border-color: var(--foisit-error-border) !important;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
.foisit-form-error {
|
|
1853
|
+
font-size: 11px;
|
|
1854
|
+
color: var(--foisit-error-text);
|
|
1855
|
+
margin-top: 4px;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/* Loading */
|
|
1859
|
+
.foisit-loading-dots {
|
|
1860
|
+
display: inline-flex;
|
|
1861
|
+
gap: 4px;
|
|
1862
|
+
padding: 10px 14px;
|
|
1863
|
+
align-self: flex-start;
|
|
1864
|
+
}
|
|
1865
|
+
.foisit-dot {
|
|
1866
|
+
width: 6px;
|
|
1867
|
+
height: 6px;
|
|
1868
|
+
border-radius: 50%;
|
|
1869
|
+
background: var(--foisit-text);
|
|
1870
|
+
opacity: 0.4;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/* Floating Button */
|
|
1874
|
+
.foisit-floating-btn {
|
|
1875
|
+
position: absolute;
|
|
1876
|
+
width: 56px;
|
|
1877
|
+
height: 56px;
|
|
1878
|
+
border-radius: 50%;
|
|
1879
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
1880
|
+
background: var(--foisit-bg);
|
|
1881
|
+
color: var(--foisit-text);
|
|
1882
|
+
backdrop-filter: blur(10px);
|
|
1883
|
+
-webkit-backdrop-filter: blur(10px);
|
|
1884
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
1885
|
+
cursor: pointer;
|
|
1886
|
+
pointer-events: auto;
|
|
1887
|
+
display: flex;
|
|
1888
|
+
align-items: center;
|
|
1889
|
+
justify-content: center;
|
|
1890
|
+
font-size: 24px;
|
|
1891
|
+
z-index: 100000;
|
|
1892
|
+
transition: transform 0.2s;
|
|
1893
|
+
}
|
|
1894
|
+
.foisit-floating-btn:hover { transform: scale(1.05); }
|
|
1895
|
+
`;
|
|
1896
|
+
document.head.appendChild(style);
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
// dist/libs/angular-wrapper/fesm2022/foisit-angular-wrapper.mjs
|
|
1901
|
+
var AngularWrapperComponent = class _AngularWrapperComponent {
|
|
1902
|
+
static \u0275fac = i0.\u0275\u0275ngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: _AngularWrapperComponent, deps: [], target: i0.\u0275\u0275FactoryTarget.Component });
|
|
1903
|
+
static \u0275cmp = i0.\u0275\u0275ngDeclareComponent({ minVersion: "14.0.0", version: "19.0.7", type: _AngularWrapperComponent, isStandalone: true, selector: "lib-angular-wrapper", ngImport: i0, template: "<p>AngularWrapper works!</p>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1904
|
+
};
|
|
1905
|
+
i0.\u0275\u0275ngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AngularWrapperComponent, decorators: [{
|
|
1906
|
+
type: Component,
|
|
1907
|
+
args: [{ selector: "lib-angular-wrapper", imports: [CommonModule], template: "<p>AngularWrapper works!</p>\n" }]
|
|
1908
|
+
}] });
|
|
1909
|
+
var AssistantService = class _AssistantService {
|
|
1910
|
+
config;
|
|
1911
|
+
commandHandler;
|
|
1912
|
+
fallbackHandler;
|
|
1913
|
+
voiceProcessor;
|
|
1914
|
+
textToSpeech;
|
|
1915
|
+
stateManager;
|
|
1916
|
+
lastProcessedInput = "";
|
|
1917
|
+
processingLock = false;
|
|
1918
|
+
isActivated = false;
|
|
1919
|
+
// Tracks activation status
|
|
1920
|
+
gestureHandler;
|
|
1921
|
+
overlayManager;
|
|
1922
|
+
defaultIntroMessage = "How can I help you?";
|
|
1923
|
+
constructor(config) {
|
|
1924
|
+
this.config = config;
|
|
1925
|
+
this.commandHandler = new CommandHandler({
|
|
1926
|
+
enableSmartIntent: this.config.enableSmartIntent !== false,
|
|
1927
|
+
intentEndpoint: this.config.intentEndpoint
|
|
1928
|
+
});
|
|
1929
|
+
this.fallbackHandler = new FallbackHandler();
|
|
1930
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
1931
|
+
this.voiceProcessor = new VoiceProcessor();
|
|
1932
|
+
this.textToSpeech = new TextToSpeech();
|
|
1933
|
+
this.gestureHandler = new GestureHandler();
|
|
1934
|
+
this.overlayManager = new OverlayManager({
|
|
1935
|
+
floatingButton: this.config.floatingButton,
|
|
1936
|
+
inputPlaceholder: this.config.inputPlaceholder
|
|
1937
|
+
});
|
|
1938
|
+
this.overlayManager.registerCallbacks(async (input) => {
|
|
1939
|
+
if (typeof input === "string") {
|
|
1940
|
+
this.overlayManager.addMessage(input, "user");
|
|
1941
|
+
} else if (input && typeof input === "object") {
|
|
1942
|
+
const label = this.extractUserLabel(input);
|
|
1943
|
+
if (label) {
|
|
1944
|
+
this.overlayManager.addMessage(label, "user");
|
|
1945
|
+
}
|
|
224
1946
|
}
|
|
225
|
-
this.
|
|
1947
|
+
await this.handleCommand(input);
|
|
1948
|
+
}, () => console.log("AssistantService: Overlay closed."));
|
|
1949
|
+
this.stateManager = new StateManager();
|
|
1950
|
+
this.gestureHandler.setupDoubleTapListener(() => this.toggle());
|
|
1951
|
+
} else {
|
|
1952
|
+
this.stateManager = void 0;
|
|
1953
|
+
this.voiceProcessor = void 0;
|
|
1954
|
+
this.textToSpeech = void 0;
|
|
1955
|
+
this.gestureHandler = void 0;
|
|
1956
|
+
this.overlayManager = void 0;
|
|
226
1957
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.commandHandler.removeCommand(command);
|
|
1958
|
+
this.config.commands.forEach((cmd) => this.commandHandler.addCommand(cmd));
|
|
1959
|
+
if (this.config.fallbackResponse) {
|
|
1960
|
+
this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse);
|
|
231
1961
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
1962
|
+
}
|
|
1963
|
+
/** Start listening for activation and commands */
|
|
1964
|
+
startListening() {
|
|
1965
|
+
if (typeof window === "undefined" || !this.voiceProcessor) {
|
|
1966
|
+
console.log("AssistantService: Voice is disabled or unavailable; startListening() is a no-op.");
|
|
1967
|
+
return;
|
|
235
1968
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
1969
|
+
}
|
|
1970
|
+
/** Stop listening for voice input */
|
|
1971
|
+
stopListening() {
|
|
1972
|
+
if (typeof window === "undefined" || !this.voiceProcessor) {
|
|
1973
|
+
console.log("AssistantService: Voice unavailable; stopListening() is a no-op.");
|
|
1974
|
+
this.isActivated = false;
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
this.voiceProcessor.stopListening();
|
|
1978
|
+
this.isActivated = false;
|
|
1979
|
+
}
|
|
1980
|
+
/** Reset activation state so the next activation flow can occur. */
|
|
1981
|
+
reactivate() {
|
|
1982
|
+
console.log("AssistantService: Reactivating assistant...");
|
|
1983
|
+
this.isActivated = false;
|
|
1984
|
+
try {
|
|
1985
|
+
this.startListening();
|
|
1986
|
+
} catch {
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
/** Process activation command */
|
|
1990
|
+
async processActivation(transcript) {
|
|
1991
|
+
const activationCmd = this.config.activationCommand?.toLowerCase();
|
|
1992
|
+
if (!activationCmd)
|
|
1993
|
+
return;
|
|
1994
|
+
if (transcript === activationCmd) {
|
|
1995
|
+
console.log("AssistantService: Activation matched.");
|
|
1996
|
+
this.isActivated = true;
|
|
1997
|
+
this.textToSpeech.speak(this.config.introMessage || this.defaultIntroMessage);
|
|
1998
|
+
} else {
|
|
1999
|
+
console.log("AssistantService: Activation command not recognized.");
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
/** Handle recognized commands */
|
|
2003
|
+
async handleCommand(input) {
|
|
2004
|
+
this.overlayManager.showLoading();
|
|
2005
|
+
let response;
|
|
2006
|
+
try {
|
|
2007
|
+
response = await this.commandHandler.executeCommand(input);
|
|
2008
|
+
} finally {
|
|
2009
|
+
this.overlayManager.hideLoading();
|
|
2010
|
+
}
|
|
2011
|
+
const originalText = typeof input === "string" ? input : void 0;
|
|
2012
|
+
this.processResponse(response, originalText);
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Cleanup resources
|
|
2016
|
+
*/
|
|
2017
|
+
destroy() {
|
|
2018
|
+
this.voiceProcessor?.stopListening();
|
|
2019
|
+
this.gestureHandler?.destroy();
|
|
2020
|
+
this.overlayManager?.destroy();
|
|
2021
|
+
}
|
|
2022
|
+
/** Unified response processing */
|
|
2023
|
+
processResponse(response, originalText) {
|
|
2024
|
+
if (response.type === "error" && originalText) {
|
|
2025
|
+
this.fallbackHandler.handleFallback(originalText);
|
|
2026
|
+
this.overlayManager.addMessage(this.fallbackHandler.getFallbackMessage(), "system");
|
|
2027
|
+
return;
|
|
257
2028
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
2029
|
+
if (response.type === "form" && response.fields) {
|
|
2030
|
+
this.overlayManager.addForm(response.message, response.fields, async (data) => {
|
|
2031
|
+
this.overlayManager.showLoading();
|
|
2032
|
+
let nextReponse;
|
|
2033
|
+
try {
|
|
2034
|
+
nextReponse = await this.commandHandler.executeCommand(data);
|
|
2035
|
+
} finally {
|
|
2036
|
+
this.overlayManager.hideLoading();
|
|
262
2037
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
2038
|
+
this.processResponse(nextReponse);
|
|
2039
|
+
});
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
if ((response.type === "ambiguous" || response.type === "confirm") && response.options) {
|
|
2043
|
+
if (response.message) {
|
|
2044
|
+
this.overlayManager.addMessage(response.message, "system");
|
|
2045
|
+
}
|
|
2046
|
+
this.overlayManager.addOptions(response.options);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
if (response.message) {
|
|
2050
|
+
this.overlayManager.addMessage(response.message, "system");
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
/** Add a command dynamically (supports string or rich object) */
|
|
2054
|
+
addCommand(commandOrObj, action) {
|
|
2055
|
+
if (typeof commandOrObj === "string") {
|
|
2056
|
+
console.log(`AssistantService: Adding command "${commandOrObj}".`);
|
|
2057
|
+
} else {
|
|
2058
|
+
console.log(`AssistantService: Adding rich command "${commandOrObj.command}".`);
|
|
2059
|
+
}
|
|
2060
|
+
this.commandHandler.addCommand(commandOrObj, action);
|
|
2061
|
+
}
|
|
2062
|
+
/** Remove a command dynamically */
|
|
2063
|
+
removeCommand(command) {
|
|
2064
|
+
console.log(`AssistantService: Removing command "${command}".`);
|
|
2065
|
+
this.commandHandler.removeCommand(command);
|
|
2066
|
+
}
|
|
2067
|
+
/** Get all registered commands */
|
|
2068
|
+
getCommands() {
|
|
2069
|
+
return this.commandHandler.getCommands();
|
|
2070
|
+
}
|
|
2071
|
+
/** Toggle the assistant overlay */
|
|
2072
|
+
toggle(onSubmit, onClose) {
|
|
2073
|
+
console.log("AssistantService: Toggling overlay...");
|
|
2074
|
+
this.overlayManager.toggle(async (input) => {
|
|
2075
|
+
if (typeof input === "string") {
|
|
2076
|
+
this.overlayManager.addMessage(input, "user");
|
|
2077
|
+
} else if (input && typeof input === "object") {
|
|
2078
|
+
const label = this.extractUserLabel(input);
|
|
2079
|
+
if (label) {
|
|
2080
|
+
this.overlayManager.addMessage(label, "user");
|
|
266
2081
|
}
|
|
267
|
-
|
|
2082
|
+
}
|
|
2083
|
+
if (onSubmit)
|
|
2084
|
+
onSubmit(input);
|
|
2085
|
+
await this.handleCommand(input);
|
|
2086
|
+
}, () => {
|
|
2087
|
+
console.log("AssistantService: Overlay closed.");
|
|
2088
|
+
if (onClose)
|
|
2089
|
+
onClose();
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
extractUserLabel(payload) {
|
|
2093
|
+
const label = payload["label"];
|
|
2094
|
+
if (typeof label === "string" && label.trim()) {
|
|
2095
|
+
return label.trim();
|
|
268
2096
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantService, decorators: [{
|
|
273
|
-
type: Injectable,
|
|
274
|
-
args: [{
|
|
275
|
-
providedIn: 'root',
|
|
276
|
-
}]
|
|
277
|
-
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
278
|
-
type: Inject,
|
|
279
|
-
args: ['ASSISTANT_CONFIG']
|
|
280
|
-
}] }] });
|
|
281
|
-
|
|
282
|
-
class AssistantModule {
|
|
283
|
-
static forRoot(config) {
|
|
284
|
-
return {
|
|
285
|
-
ngModule: AssistantModule,
|
|
286
|
-
providers: [
|
|
287
|
-
{ provide: 'ASSISTANT_CONFIG', useValue: config },
|
|
288
|
-
AssistantService,
|
|
289
|
-
],
|
|
290
|
-
};
|
|
2097
|
+
const commandId = payload["commandId"];
|
|
2098
|
+
if (typeof commandId === "string" && commandId.trim()) {
|
|
2099
|
+
return commandId.trim();
|
|
291
2100
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
2101
|
+
return null;
|
|
2102
|
+
}
|
|
2103
|
+
static \u0275fac = i0.\u0275\u0275ngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: _AssistantService, deps: [{ token: "ASSISTANT_CONFIG" }], target: i0.\u0275\u0275FactoryTarget.Injectable });
|
|
2104
|
+
static \u0275prov = i0.\u0275\u0275ngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: _AssistantService, providedIn: "root" });
|
|
2105
|
+
};
|
|
2106
|
+
i0.\u0275\u0275ngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantService, decorators: [{
|
|
2107
|
+
type: Injectable,
|
|
2108
|
+
args: [{
|
|
2109
|
+
providedIn: "root"
|
|
2110
|
+
}]
|
|
2111
|
+
}], ctorParameters: () => [{ type: void 0, decorators: [{
|
|
2112
|
+
type: Inject,
|
|
2113
|
+
args: ["ASSISTANT_CONFIG"]
|
|
2114
|
+
}] }] });
|
|
2115
|
+
var AssistantModule = class _AssistantModule {
|
|
2116
|
+
static forRoot(config) {
|
|
2117
|
+
return {
|
|
2118
|
+
ngModule: _AssistantModule,
|
|
2119
|
+
providers: [
|
|
2120
|
+
{ provide: "ASSISTANT_CONFIG", useValue: config },
|
|
2121
|
+
AssistantService
|
|
2122
|
+
]
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
static \u0275fac = i0.\u0275\u0275ngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: _AssistantModule, deps: [], target: i0.\u0275\u0275FactoryTarget.NgModule });
|
|
2126
|
+
static \u0275mod = i0.\u0275\u0275ngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.7", ngImport: i0, type: _AssistantModule });
|
|
2127
|
+
static \u0275inj = i0.\u0275\u0275ngDeclareInjector({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: _AssistantModule });
|
|
2128
|
+
};
|
|
2129
|
+
i0.\u0275\u0275ngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: AssistantModule, decorators: [{
|
|
2130
|
+
type: NgModule,
|
|
2131
|
+
args: [{}]
|
|
2132
|
+
}] });
|
|
2133
|
+
export {
|
|
2134
|
+
AngularWrapperComponent,
|
|
2135
|
+
AssistantModule,
|
|
2136
|
+
AssistantService
|
|
2137
|
+
};
|
|
306
2138
|
//# sourceMappingURL=foisit-angular-wrapper.mjs.map
|