reneco-advanced-input-module 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.editorconfig +15 -0
  2. package/.prettierrc.json +13 -0
  3. package/LICENSE +21 -0
  4. package/api-key-inject.js +46 -0
  5. package/dist/cjs/app-globals-V2Kpy_OQ.js +8 -0
  6. package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
  7. package/dist/cjs/file-uploader.voice-input-module.entry.cjs.js.map +1 -0
  8. package/dist/cjs/file-uploader_2.cjs.entry.js +1319 -0
  9. package/dist/cjs/file-uploader_2.cjs.entry.js.map +1 -0
  10. package/dist/cjs/index-BTSzTkSZ.js +1494 -0
  11. package/dist/cjs/index-BTSzTkSZ.js.map +1 -0
  12. package/dist/cjs/index.cjs.js +5 -0
  13. package/dist/cjs/index.cjs.js.map +1 -0
  14. package/dist/cjs/loader.cjs.js +16 -0
  15. package/dist/cjs/loader.cjs.js.map +1 -0
  16. package/dist/cjs/voice-input-module.cjs.js +28 -0
  17. package/dist/cjs/voice-input-module.cjs.js.map +1 -0
  18. package/dist/collection/collection-manifest.json +13 -0
  19. package/dist/collection/components/file-uploader/file-uploader.css +26 -0
  20. package/dist/collection/components/file-uploader/file-uploader.js +130 -0
  21. package/dist/collection/components/file-uploader/file-uploader.js.map +1 -0
  22. package/dist/collection/components/voice-input-module/voice-input-module.css +251 -0
  23. package/dist/collection/components/voice-input-module/voice-input-module.js +875 -0
  24. package/dist/collection/components/voice-input-module/voice-input-module.js.map +1 -0
  25. package/dist/collection/index.js +12 -0
  26. package/dist/collection/index.js.map +1 -0
  27. package/dist/collection/services/audio-recorder.service.js +66 -0
  28. package/dist/collection/services/audio-recorder.service.js.map +1 -0
  29. package/dist/collection/services/llm.service.js +193 -0
  30. package/dist/collection/services/llm.service.js.map +1 -0
  31. package/dist/collection/services/speech-to-text.service.js +62 -0
  32. package/dist/collection/services/speech-to-text.service.js.map +1 -0
  33. package/dist/collection/types/form-schema.types.js +2 -0
  34. package/dist/collection/types/form-schema.types.js.map +1 -0
  35. package/dist/collection/types/service-providers.types.js +2 -0
  36. package/dist/collection/types/service-providers.types.js.map +1 -0
  37. package/dist/collection/utils/schema-converter.js +422 -0
  38. package/dist/collection/utils/schema-converter.js.map +1 -0
  39. package/dist/components/file-uploader.d.ts +11 -0
  40. package/dist/components/file-uploader.js +9 -0
  41. package/dist/components/file-uploader.js.map +1 -0
  42. package/dist/components/file-uploader2.js +98 -0
  43. package/dist/components/file-uploader2.js.map +1 -0
  44. package/dist/components/index.d.ts +33 -0
  45. package/dist/components/index.js +4 -0
  46. package/dist/components/index.js.map +1 -0
  47. package/dist/components/voice-input-module.d.ts +11 -0
  48. package/dist/components/voice-input-module.js +1292 -0
  49. package/dist/components/voice-input-module.js.map +1 -0
  50. package/dist/esm/app-globals-DQuL1Twl.js +6 -0
  51. package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
  52. package/dist/esm/file-uploader.voice-input-module.entry.js.map +1 -0
  53. package/dist/esm/file-uploader_2.entry.js +1316 -0
  54. package/dist/esm/file-uploader_2.entry.js.map +1 -0
  55. package/dist/esm/index-jmc2yzBp.js +1487 -0
  56. package/dist/esm/index-jmc2yzBp.js.map +1 -0
  57. package/dist/esm/index.js +4 -0
  58. package/dist/esm/index.js.map +1 -0
  59. package/dist/esm/loader.js +14 -0
  60. package/dist/esm/loader.js.map +1 -0
  61. package/dist/esm/voice-input-module.js +24 -0
  62. package/dist/esm/voice-input-module.js.map +1 -0
  63. package/dist/index.cjs.js +1 -0
  64. package/dist/index.js +1 -0
  65. package/dist/types/components/file-uploader/file-uploader.d.ts +8 -0
  66. package/dist/types/components/voice-input-module/voice-input-module.d.ts +55 -0
  67. package/dist/types/components.d.ts +158 -0
  68. package/dist/types/index.d.ts +9 -0
  69. package/dist/types/services/audio-recorder.service.d.ts +9 -0
  70. package/dist/types/services/llm.service.d.ts +15 -0
  71. package/dist/types/services/speech-to-text.service.d.ts +11 -0
  72. package/dist/types/stencil-public-runtime.d.ts +1709 -0
  73. package/dist/types/types/form-schema.types.d.ts +70 -0
  74. package/dist/types/types/service-providers.types.d.ts +20 -0
  75. package/dist/types/utils/schema-converter.d.ts +22 -0
  76. package/dist/voice-input-module/file-uploader.voice-input-module.entry.esm.js.map +1 -0
  77. package/dist/voice-input-module/index.esm.js +2 -0
  78. package/dist/voice-input-module/index.esm.js.map +1 -0
  79. package/dist/voice-input-module/loader.esm.js.map +1 -0
  80. package/dist/voice-input-module/p-7b4f33ba.entry.js +2 -0
  81. package/dist/voice-input-module/p-7b4f33ba.entry.js.map +1 -0
  82. package/dist/voice-input-module/p-DQuL1Twl.js +2 -0
  83. package/dist/voice-input-module/p-DQuL1Twl.js.map +1 -0
  84. package/dist/voice-input-module/p-jmc2yzBp.js +3 -0
  85. package/dist/voice-input-module/p-jmc2yzBp.js.map +1 -0
  86. package/dist/voice-input-module/voice-input-module.esm.js +2 -0
  87. package/dist/voice-input-module/voice-input-module.esm.js.map +1 -0
  88. package/env-config.js +4 -0
  89. package/inject-env.js +20 -0
  90. package/package.json +37 -0
  91. package/readme.md +111 -0
  92. package/src/components/file-uploader/file-uploader.css +26 -0
  93. package/src/components/file-uploader/file-uploader.tsx +100 -0
  94. package/src/components/file-uploader/readme.md +31 -0
  95. package/src/components/voice-input-module/readme.md +114 -0
  96. package/src/components/voice-input-module/voice-input-module.css +251 -0
  97. package/src/components/voice-input-module/voice-input-module.tsx +731 -0
  98. package/src/components.d.ts +158 -0
  99. package/src/index.html +663 -0
  100. package/src/index.ts +12 -0
  101. package/src/services/audio-recorder.service.ts +74 -0
  102. package/src/services/llm.service.ts +221 -0
  103. package/src/services/speech-to-text.service.ts +72 -0
  104. package/src/types/form-schema.types.ts +78 -0
  105. package/src/types/service-providers.types.ts +22 -0
  106. package/src/utils/schema-converter.ts +494 -0
  107. package/stencil.config.ts +24 -0
  108. package/tsconfig.json +30 -0
@@ -0,0 +1,1316 @@
1
+ import { r as registerInstance, h, c as createEvent } from './index-jmc2yzBp.js';
2
+
3
+ const fileUploaderCss = ".upload-container{display:inline-block;cursor:pointer;width:50px;height:50px}.upload-button{display:flex;align-items:center;justify-content:center;transition:transform 0.2s ease, box-shadow 0.2s ease;user-select:none}.upload-button svg{width:50px;height:50px;stroke:#44ee44}.upload-button:hover svg{transform:scale(1.05);stroke:#33dd33}";
4
+
5
+ const FileUploader = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.batch = false;
9
+ this.triggerUpload = () => {
10
+ this.fileInput.click();
11
+ };
12
+ this.handleFileChange = async (event) => {
13
+ const input = event.target;
14
+ if (!input.files || input.files.length === 0)
15
+ return;
16
+ const file = input.files[0];
17
+ // Here you can handle the file upload to your API
18
+ console.log('Selected file:', file);
19
+ const formData = new FormData();
20
+ formData.append('file', file);
21
+ if (this.batch) {
22
+ try {
23
+ const response = await fetch('http://127.0.0.1:5001/api/convert-to-xml', {
24
+ method: 'POST',
25
+ body: formData
26
+ });
27
+ if (!response.ok) {
28
+ console.error("There has been an error!:", response);
29
+ throw new Error(`HTTP error! status: ${response.status}`);
30
+ }
31
+ // get the file as a blob
32
+ const blob = await response.blob();
33
+ // create a download link
34
+ const url = window.URL.createObjectURL(blob);
35
+ const a = document.createElement('a');
36
+ a.href = url;
37
+ a.download = 'result-' + Date.now().toString() + '.xlsx'; // filename
38
+ document.body.appendChild(a);
39
+ a.click();
40
+ a.remove();
41
+ window.URL.revokeObjectURL(url);
42
+ }
43
+ catch (err) {
44
+ console.error(err);
45
+ alert('Error uploading file');
46
+ }
47
+ }
48
+ else {
49
+ try {
50
+ const response = await fetch('http://127.0.0.1:5001/api/convert-to-json', {
51
+ method: 'POST',
52
+ body: formData
53
+ });
54
+ if (!response.ok) {
55
+ console.error("There has been an error!:", response);
56
+ throw new Error(`HTTP error! status: ${response.status}`);
57
+ }
58
+ const data = await response.json();
59
+ console.log('Upload successful:', data);
60
+ if (this.callback && data[0] && data[0].content) {
61
+ this.callback(data[0].content);
62
+ }
63
+ }
64
+ catch (err) {
65
+ console.error(err);
66
+ alert('Error uploading file');
67
+ }
68
+ }
69
+ };
70
+ }
71
+ render() {
72
+ return (h("div", { key: '1f7afd7bce2dfa48fc8a0d05c40c6f12497af499', class: "upload-container", onClick: this.triggerUpload }, h("input", { key: 'd33effe28d1492066348ee234102d3f54607602f', type: "file", ref: el => (this.fileInput = el), onChange: this.handleFileChange, style: { display: 'none' } }), h("div", { key: '0031ca27ec1f0a36425920d2507f50eecdbcebea', class: "upload-button" }, h("svg", { key: '521b55b8e9ab9e04c35ff1fd5ea141d73d7e187f', viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, h("path", { key: '67ca01d5b2967fdf994c84edc5dadab90bd2387f', d: "M13.5 3H12H8C6.34315 3 5 4.34315 5 6V18C5 19.6569 6.34315 21 8 21H12M13.5 3L19 8.625M13.5 3V7.625C13.5 8.17728 13.9477 8.625 14.5 8.625H19M19 8.625V11.8125", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { key: '09e80b7a02e45d09cb7575398061dbc954a24d47', d: "M17.5 21L17.5 15M17.5 15L20 17.5M17.5 15L15 17.5", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" })))));
73
+ }
74
+ };
75
+ FileUploader.style = fileUploaderCss;
76
+
77
+ class AudioRecorderService {
78
+ constructor() {
79
+ this.mediaRecorder = null;
80
+ this.audioChunks = [];
81
+ this.stream = null;
82
+ }
83
+ async startRecording() {
84
+ try {
85
+ // Check if the API exists before calling
86
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
87
+ console.error('Failed to start recording:', 'Microphone access is not supported in this browser or the page is not served over HTTPS/localhost.');
88
+ return; // Exit gracefully instead of throwing
89
+ }
90
+ this.stream = await navigator.mediaDevices.getUserMedia({
91
+ audio: {
92
+ echoCancellation: true,
93
+ noiseSuppression: true,
94
+ autoGainControl: true
95
+ }
96
+ });
97
+ this.audioChunks = [];
98
+ this.mediaRecorder = new MediaRecorder(this.stream, {
99
+ mimeType: 'audio/webm;codecs=opus'
100
+ });
101
+ this.mediaRecorder.ondataavailable = (event) => {
102
+ if (event.data && event.data.size > 0) {
103
+ this.audioChunks.push(event.data);
104
+ }
105
+ };
106
+ this.mediaRecorder.start(100); // Collect data every 100ms
107
+ }
108
+ catch (error) {
109
+ console.error('Failed to start recording:', error);
110
+ }
111
+ }
112
+ async stopRecording() {
113
+ return new Promise((resolve, reject) => {
114
+ if (!this.mediaRecorder) {
115
+ reject(new Error('No active recording found'));
116
+ return;
117
+ }
118
+ this.mediaRecorder.onstop = () => {
119
+ const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });
120
+ this.cleanup();
121
+ resolve(audioBlob);
122
+ };
123
+ this.mediaRecorder.onerror = (event) => {
124
+ reject(new Error(`Recording error: ${event}`));
125
+ };
126
+ this.mediaRecorder.stop();
127
+ });
128
+ }
129
+ isRecording() {
130
+ var _a;
131
+ return ((_a = this.mediaRecorder) === null || _a === void 0 ? void 0 : _a.state) === 'recording';
132
+ }
133
+ cleanup() {
134
+ if (this.stream) {
135
+ this.stream.getTracks().forEach(track => track.stop());
136
+ this.stream = null;
137
+ }
138
+ this.mediaRecorder = null;
139
+ this.audioChunks = [];
140
+ }
141
+ }
142
+
143
+ class WhisperSpeechToTextService {
144
+ constructor(config) {
145
+ // Get API key from config or environment
146
+ this.apiKey = (config === null || config === void 0 ? void 0 : config.apiKey) || this.getEnvironmentVariable('OPENAI_API_KEY') || '';
147
+ this.baseUrl = (config === null || config === void 0 ? void 0 : config.baseUrl) || 'https://api.openai.com/v1';
148
+ if (!this.apiKey) {
149
+ throw new Error('OpenAI API key is required for Whisper service');
150
+ }
151
+ }
152
+ getEnvironmentVariable(name) {
153
+ // In browser environment, we might get env vars through other means
154
+ if (typeof process !== 'undefined' && process.env) {
155
+ return process.env[name];
156
+ }
157
+ // Check if it's available as a global variable or through other means
158
+ return window[name] || undefined;
159
+ }
160
+ async transcribe(audioBlob, lang = 'en') {
161
+ var _a;
162
+ try {
163
+ const formData = new FormData();
164
+ // Convert webm to a format Whisper can handle
165
+ const audioFile = new File([audioBlob], 'audio.webm', { type: 'audio/webm' });
166
+ formData.append('file', audioFile);
167
+ formData.append('model', 'gpt-4o-transcribe'); // >>> tronque le texte ?
168
+ // formData.append('model', 'gpt-4o-mini-transcribe');// >>> tronque le texte ?
169
+ // formData.append('model', 'whisper-1');
170
+ formData.append('language', lang);
171
+ formData.append('response_format', 'json');
172
+ formData.append('max_output_tokens', '2000');
173
+ const response = await fetch(`${this.baseUrl}/audio/transcriptions`, {
174
+ method: 'POST',
175
+ headers: {
176
+ 'Authorization': `Bearer ${this.apiKey}`,
177
+ },
178
+ body: formData,
179
+ });
180
+ if (!response.ok) {
181
+ const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
182
+ throw new Error(`Transcription failed: ${((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.message) || response.statusText}`);
183
+ }
184
+ const result = await response.json();
185
+ return result.text || '';
186
+ }
187
+ catch (error) {
188
+ throw new Error(`Speech-to-text transcription failed: ${error.message}`);
189
+ }
190
+ }
191
+ }
192
+ class SpeechToTextServiceFactory {
193
+ static create(config) {
194
+ var _a;
195
+ const provider = ((_a = config.speechToText) === null || _a === void 0 ? void 0 : _a.provider) || 'whisper';
196
+ switch (provider) {
197
+ case 'whisper':
198
+ return new WhisperSpeechToTextService(config.speechToText);
199
+ default:
200
+ throw new Error(`Unsupported speech-to-text provider: ${provider}`);
201
+ }
202
+ }
203
+ }
204
+
205
+ class OpenAILLMService {
206
+ constructor(config) {
207
+ // Get API key from config or environment
208
+ this.apiKey = (config === null || config === void 0 ? void 0 : config.apiKey) || this.getEnvironmentVariable('OPENAI_API_KEY') || '';
209
+ // the newest OpenAI model is "gpt-4.1-mini". do not change this unless explicitly requested by the user
210
+ // this.model = config?.model || 'gpt-4.1-mini';
211
+ this.model = (config === null || config === void 0 ? void 0 : config.model) || 'gpt-4.1';
212
+ this.baseUrl = (config === null || config === void 0 ? void 0 : config.baseUrl) || 'https://api.openai.com/v1';
213
+ if (!this.apiKey) {
214
+ throw new Error('OpenAI API key is required for LLM service');
215
+ }
216
+ }
217
+ getEnvironmentVariable(name) {
218
+ // In browser environment, we might get env vars through other means
219
+ if (typeof process !== 'undefined' && process.env) {
220
+ return process.env[name];
221
+ }
222
+ // Check if it's available as a global variable or through other means
223
+ return window[name] || undefined;
224
+ }
225
+ getOptimizeFieldsDescription(schema) {
226
+ return Object.values(schema).map((field) => {
227
+ var _a;
228
+ return `- ${(_a = field.name) !== null && _a !== void 0 ? _a : field.title} ` +
229
+ `(${field.type}` +
230
+ `${field.required ? ', required' : ''}` +
231
+ `${field.readonly ? ', readonly' : ''}` +
232
+ `${field.min && field.min !== "" ? ', min=' + field.min : ''}` +
233
+ `${field.max && field.max !== "" ? ', max=' + field.max : ''}` +
234
+ `)` +
235
+ `${field.options ? ` - options: ${field.options.join(', ')}` : ''}`;
236
+ }).join('\n');
237
+ }
238
+ async fillFormFromTranscription(transcription, schema) {
239
+ return this.fillForm(transcription, schema, true);
240
+ }
241
+ async fillFormFromJson(json, schema) {
242
+ return this.fillForm(json, schema, false);
243
+ }
244
+ async fillForm(data, schema, dataIsTranscription = true) {
245
+ var _a, _b;
246
+ try {
247
+ // Handle complex schema format with fields array
248
+ if ((schema === null || schema === void 0 ? void 0 : schema.fields) || (schema === null || schema === void 0 ? void 0 : schema.schema)) {
249
+ const finalSchema = (schema === null || schema === void 0 ? void 0 : schema.fields) || (schema === null || schema === void 0 ? void 0 : schema.schema);
250
+ const systemPrompt = `You are an expert form-filling assistant. You will receive a voice transcription and form field definitions. Your task is to extract values from the spoken content.
251
+ Rules:
252
+ 1. Only extract values that can be confidently determined from the transcription
253
+ 2. Respect field types (string, number, datetime, boolean, select)
254
+ 3. For datetime fields, use ISO format (YYYY-MM-DDTHH:MM)
255
+ 4. For date fields, use the following format: DD/MM/YYY
256
+ 5. For boolean fields, interpret yes/no, true/false, positive/negative responses
257
+ 6. For select fields, use exact option values from the provided choices
258
+ 7. Only include fields where relevant information is found
259
+ 8. The current GMT datetime is ${new Date().toGMTString()}
260
+ 9. Respect the constraints written between parenthesis for readonly status (readonly fields must not be filled with values), min and max values, whatever the transcription says
261
+
262
+ Respond with JSON in this exact format: {"fields": [{"name": "field_name", "value": "extracted_value"}]}`;
263
+ let userPrompt = `
264
+ Voice transcription: "${data}"
265
+
266
+ Form fields:
267
+ ${this.getOptimizeFieldsDescription(finalSchema)}
268
+
269
+ Please extract values from the Transcription for these fields.
270
+ `;
271
+ //TODO
272
+ // const userPrompt = (
273
+ // dataIsTranscription
274
+ // ?
275
+ // `Voice transcription: "${data}"`
276
+ // :
277
+ // `Json datas: "${JSON.stringify(data)}"`
278
+ // )+`
279
+ // Form fields:
280
+ // ${this.getOptimizeFieldsDescription(finalSchema)}
281
+ // Please extract values from the ` +
282
+ // (
283
+ // dataIsTranscription
284
+ // ?
285
+ // `Transcription`
286
+ // :
287
+ // `Json generated file`
288
+ // )+` for these fields.`;
289
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
290
+ method: 'POST',
291
+ headers: {
292
+ 'Content-Type': 'application/json',
293
+ 'Authorization': `Bearer ${this.apiKey}`,
294
+ },
295
+ body: JSON.stringify({
296
+ model: this.model,
297
+ messages: [
298
+ { role: 'system', content: systemPrompt },
299
+ { role: 'user', content: userPrompt }
300
+ ],
301
+ response_format: { type: 'json_object' },
302
+ temperature: 0.1,
303
+ }),
304
+ });
305
+ if (!response.ok) {
306
+ const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
307
+ throw new Error(`LLM API failed: ${((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.message) || response.statusText}`);
308
+ }
309
+ const result = await response.json();
310
+ return JSON.parse(result.choices[0].message.content);
311
+ }
312
+ // Handle simple schema format (backward compatibility)
313
+ const systemPrompt = `You are an expert form-filling assistant. You will receive a voice transcription and a JSON form schema. Your task is to intelligently fill the form fields based on the spoken content.
314
+ Rules:
315
+ 1. Only fill fields that can be confidently determined from the transcription
316
+ 2. Respect field types (string, number, date, boolean, select)
317
+ 3. For datetime fields, use ISO format (YYYY-MM-DDTHH:MM)
318
+ 4. For date fields, use the following format: DD/MM/YYY
319
+ 5. For boolean fields, interpret yes/no, true/false, positive/negative responses
320
+ 6. For select fields, match the closest option from the provided choices
321
+ 7. Leave fields empty if no relevant information is found
322
+ 8. Return the same schema structure with 'default' values filled
323
+ 9. The current GMT datetime is ${new Date().toGMTString()}
324
+ 10. Respect the constraints written between parenthesis for readonly status (readonly fields must not be filled with values), min and max values, whatever the transcription says
325
+
326
+ Respond with JSON in this exact format: {"schema": {...}}`;
327
+ let userPrompt = `
328
+ Voice transcription: "${data}"
329
+
330
+ Form schema to fill:
331
+ ${JSON.stringify(schema, null, 2)}
332
+
333
+ Please fill the form fields based on the Transcription and return the schema with default values populated.
334
+ `;
335
+ // const userPrompt = (
336
+ // dataIsTranscription
337
+ // ?
338
+ // `Voice transcription: "${data}"`
339
+ // :
340
+ // `Json datas: "${JSON.stringify(data)}"`
341
+ // )+`
342
+ // Form schema to fill:
343
+ // ${JSON.stringify(schema, null, 2)}
344
+ // Please fill the form fields based on the ` +
345
+ // (
346
+ // dataIsTranscription
347
+ // ?
348
+ // `Transcription`
349
+ // :
350
+ // `Json generated file`
351
+ // )+` nd return the schema with default values populated.`;
352
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
353
+ method: 'POST',
354
+ headers: {
355
+ 'Content-Type': 'application/json',
356
+ 'Authorization': `Bearer ${this.apiKey}`,
357
+ },
358
+ body: JSON.stringify({
359
+ model: this.model,
360
+ messages: [
361
+ { role: 'system', content: systemPrompt },
362
+ { role: 'user', content: userPrompt }
363
+ ],
364
+ response_format: { type: 'json_object' },
365
+ temperature: 0.1, // Low temperature for consistency
366
+ }),
367
+ });
368
+ if (!response.ok) {
369
+ const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
370
+ throw new Error(`LLM API failed: ${((_b = errorData.error) === null || _b === void 0 ? void 0 : _b.message) || response.statusText}`);
371
+ }
372
+ const result = await response.json();
373
+ const filledSchema = JSON.parse(result.choices[0].message.content);
374
+ // Validate that the response has the correct structure
375
+ if (!filledSchema.schema) {
376
+ throw new Error('Invalid response format from LLM service');
377
+ }
378
+ return filledSchema;
379
+ }
380
+ catch (error) {
381
+ throw new Error(`Form filling failed: ${error.message}`);
382
+ }
383
+ }
384
+ }
385
+ class LLMServiceFactory {
386
+ static create(config) {
387
+ var _a;
388
+ const provider = ((_a = config.llm) === null || _a === void 0 ? void 0 : _a.provider) || 'openai';
389
+ switch (provider) {
390
+ case 'openai':
391
+ return new OpenAILLMService(config.llm);
392
+ default:
393
+ throw new Error(`Unsupported LLM provider: ${provider}`);
394
+ }
395
+ }
396
+ }
397
+
398
+ class SchemaConverter {
399
+ /**
400
+ * Convert XML form definition to JSON schema
401
+ */
402
+ static convertXmlToJsonLegacy(xmlForm) {
403
+ try {
404
+ const parser = new DOMParser();
405
+ const xmlDoc = parser.parseFromString(xmlForm, 'text/xml');
406
+ if (xmlDoc.querySelector('parsererror')) {
407
+ throw new Error('Invalid XML format');
408
+ }
409
+ const schema = {
410
+ schema: {}
411
+ };
412
+ // Extract form title and description
413
+ const formElement = xmlDoc.querySelector('form');
414
+ if (formElement) {
415
+ schema.title = formElement.getAttribute('title') || undefined;
416
+ schema.description = formElement.getAttribute('description') || undefined;
417
+ }
418
+ // Process input fields
419
+ const inputs = xmlDoc.querySelectorAll('input, select, textarea');
420
+ inputs.forEach(input => {
421
+ const name = input.getAttribute('name');
422
+ if (!name)
423
+ return;
424
+ const field = {
425
+ type: this.mapXmlTypeToJsonTypeLegacy(input.tagName.toLowerCase(), input.getAttribute('type')),
426
+ title: input.getAttribute('title') || input.getAttribute('placeholder') || name,
427
+ description: input.getAttribute('description') || undefined,
428
+ required: input.hasAttribute('required'),
429
+ default: input.getAttribute('value') || undefined
430
+ };
431
+ // Handle select options
432
+ if (input.tagName.toLowerCase() === 'select') {
433
+ const options = Array.from(input.querySelectorAll('option')).map(option => option.textContent || option.getAttribute('value') || '');
434
+ if (options.length > 0) {
435
+ field.options = options;
436
+ }
437
+ }
438
+ // Handle validation attributes
439
+ const min = input.getAttribute('min');
440
+ const max = input.getAttribute('max');
441
+ const pattern = input.getAttribute('pattern');
442
+ if (min)
443
+ field.min = parseFloat(min);
444
+ if (max)
445
+ field.max = parseFloat(max);
446
+ if (pattern)
447
+ field.pattern = pattern;
448
+ schema.schema[name] = field;
449
+ });
450
+ return schema;
451
+ }
452
+ catch (error) {
453
+ throw new Error(`XML to JSON conversion failed: ${error.message}`);
454
+ }
455
+ }
456
+ /**
457
+ * Convert JSON schema to XML form definition
458
+ */
459
+ static convertJsonToXmlLegacy(jsonForm) {
460
+ try {
461
+ const doc = document.implementation.createDocument('', '', null);
462
+ const form = doc.createElement('form');
463
+ if (jsonForm.title) {
464
+ form.setAttribute('title', jsonForm.title);
465
+ }
466
+ if (jsonForm.description) {
467
+ form.setAttribute('description', jsonForm.description);
468
+ }
469
+ Object.entries(jsonForm.schema).forEach(([fieldName, field]) => {
470
+ var _a;
471
+ // Inject default value from root if present
472
+ const valueFromRoot = jsonForm[fieldName];
473
+ const fieldWithDefault = Object.assign(Object.assign({}, field), { default: (_a = field.default) !== null && _a !== void 0 ? _a : valueFromRoot // field.default has priority
474
+ });
475
+ const element = this.createXmlElementLegacy(doc, fieldName, fieldWithDefault);
476
+ if (element) {
477
+ form.appendChild(element);
478
+ }
479
+ });
480
+ doc.appendChild(form);
481
+ const serializer = new XMLSerializer();
482
+ return serializer.serializeToString(doc);
483
+ }
484
+ catch (error) {
485
+ throw new Error(`JSON to XML conversion failed: ${error.message}`);
486
+ }
487
+ }
488
+ static mapXmlTypeToJsonTypeLegacy(tagName, type) {
489
+ switch (tagName) {
490
+ case 'select':
491
+ return 'select';
492
+ case 'textarea':
493
+ return 'string';
494
+ case 'input':
495
+ switch (type) {
496
+ case 'number':
497
+ case 'range':
498
+ return 'number';
499
+ case 'date':
500
+ case 'datetime-local':
501
+ case 'time':
502
+ return 'date';
503
+ case 'checkbox':
504
+ return 'boolean';
505
+ default:
506
+ return 'string';
507
+ }
508
+ default:
509
+ return 'string';
510
+ }
511
+ }
512
+ static createXmlElementLegacy(doc, fieldName, field) {
513
+ let element;
514
+ switch (field.type) {
515
+ case 'select':
516
+ element = doc.createElement('select');
517
+ if (field.options) {
518
+ field.options.forEach(option => {
519
+ const optionElement = doc.createElement('option');
520
+ optionElement.setAttribute('value', option);
521
+ optionElement.textContent = option;
522
+ // Set selected if it matches the default value
523
+ if (field.default !== undefined && field.default === option) {
524
+ optionElement.setAttribute('selected', 'true');
525
+ }
526
+ element.appendChild(optionElement);
527
+ });
528
+ }
529
+ break;
530
+ case 'boolean':
531
+ element = doc.createElement('input');
532
+ element.setAttribute('type', 'checkbox');
533
+ if (field.default === true || field.default === 'true') {
534
+ element.setAttribute('checked', 'true');
535
+ }
536
+ break;
537
+ case 'number':
538
+ element = doc.createElement('input');
539
+ element.setAttribute('type', 'number');
540
+ if (field.min !== undefined)
541
+ element.setAttribute('min', field.min.toString());
542
+ if (field.max !== undefined)
543
+ element.setAttribute('max', field.max.toString());
544
+ if (field.default !== undefined)
545
+ element.setAttribute('value', field.default.toString());
546
+ break;
547
+ case 'date':
548
+ element = doc.createElement('input');
549
+ element.setAttribute('type', 'date');
550
+ if (field.default !== undefined)
551
+ element.setAttribute('value', field.default.toString());
552
+ break;
553
+ default: // string, text, etc.
554
+ element = doc.createElement('input');
555
+ element.setAttribute('type', 'text');
556
+ if (field.default !== undefined)
557
+ element.setAttribute('value', field.default.toString());
558
+ }
559
+ element.setAttribute('name', fieldName);
560
+ element.setAttribute('title', field.title);
561
+ if (field.description) {
562
+ element.setAttribute('description', field.description);
563
+ }
564
+ if (field.required) {
565
+ element.setAttribute('required', 'true');
566
+ }
567
+ if (field.pattern) {
568
+ element.setAttribute('pattern', field.pattern);
569
+ }
570
+ return element;
571
+ }
572
+ /**
573
+ * Convert new XML form definition to JSON schema
574
+ */
575
+ static async convertXmlToJson(xmlForm, classificationRootURL = "http://localhost", lang = 'en') {
576
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
577
+ try {
578
+ const parser = new DOMParser();
579
+ const xmlDoc = parser.parseFromString(xmlForm, 'text/xml');
580
+ if (xmlDoc.querySelector('parsererror')) {
581
+ throw new Error('Invalid XML format');
582
+ }
583
+ // Root Xforms element (optional)
584
+ const xforms = xmlDoc.querySelector('Xforms');
585
+ if (!xforms)
586
+ throw new Error('No Xforms root element found');
587
+ const fields = xforms.querySelectorAll('Fields');
588
+ if (fields.length === 0)
589
+ throw new Error('No Fields elements found');
590
+ const schema = {
591
+ fields: {}
592
+ };
593
+ for (const fieldEl of Array.from(fields)) {
594
+ // Read key info
595
+ const name = (_b = (_a = fieldEl.querySelector('SystemName')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim();
596
+ if (!name)
597
+ return;
598
+ const helpText = (_d = (_c = fieldEl.querySelector('HelpText')) === null || _c === void 0 ? void 0 : _c.textContent) === null || _d === void 0 ? void 0 : _d.trim();
599
+ const defaultValue = (_e = fieldEl.querySelector('DefaultValue')) === null || _e === void 0 ? void 0 : _e.textContent;
600
+ const enabledText = ((_g = (_f = fieldEl.querySelector('Enabled')) === null || _f === void 0 ? void 0 : _f.textContent) === null || _g === void 0 ? void 0 : _g.trim().toLowerCase()) || 'true';
601
+ const isEnabled = enabledText === 'true';
602
+ // keep as memory
603
+ const field_ID = (_h = fieldEl.querySelector('ID')) === null || _h === void 0 ? void 0 : _h.textContent;
604
+ const field_TFie_PK_ID = (_j = fieldEl.querySelector('TFie_PK_ID')) === null || _j === void 0 ? void 0 : _j.textContent;
605
+ const field_TVal_PK_ID = (_k = fieldEl.querySelector('TVal_PK_ID')) === null || _k === void 0 ? void 0 : _k.textContent;
606
+ const field_TFIn_PK_ID = (_l = fieldEl.querySelector('TFIn_PK_ID')) === null || _l === void 0 ? void 0 : _l.textContent;
607
+ const field_isForm = (_m = fieldEl.querySelector('isForm')) === null || _m === void 0 ? void 0 : _m.textContent;
608
+ const field_LabelText = (_o = fieldEl.querySelector('LabelText')) === null || _o === void 0 ? void 0 : _o.textContent;
609
+ const field_SystemName = (_p = fieldEl.querySelector('SystemName')) === null || _p === void 0 ? void 0 : _p.textContent;
610
+ const field_CssStyle = (_q = fieldEl.querySelector('CssStyle')) === null || _q === void 0 ? void 0 : _q.textContent;
611
+ const field_ControlType = ((_r = fieldEl.querySelector('ControlType')) === null || _r === void 0 ? void 0 : _r.textContent) || 'TextBox';
612
+ const field_ValidationRequired = (_s = fieldEl.querySelector('ValidationRequired')) === null || _s === void 0 ? void 0 : _s.textContent;
613
+ const field_ValidationMin = (_t = fieldEl.querySelector('ValidationMin')) === null || _t === void 0 ? void 0 : _t.textContent;
614
+ const field_ValidationMax = (_u = fieldEl.querySelector('ValidationMax')) === null || _u === void 0 ? void 0 : _u.textContent;
615
+ const field_Mask = (_v = fieldEl.querySelector('Mask')) === null || _v === void 0 ? void 0 : _v.textContent;
616
+ const field_TypeId = (_w = fieldEl.querySelector('TypeId')) === null || _w === void 0 ? void 0 : _w.textContent;
617
+ const field_Unit = (_x = fieldEl.querySelector('Unit')) === null || _x === void 0 ? void 0 : _x.textContent;
618
+ const field_TFie_Fullpath = (_y = fieldEl.querySelector('TFie_Fullpath')) === null || _y === void 0 ? void 0 : _y.textContent;
619
+ const field_TVal_FK_Parent_ID = (_z = fieldEl.querySelector('TVal_FK_Parent_ID')) === null || _z === void 0 ? void 0 : _z.textContent;
620
+ const field = {
621
+ type: this.mapControlTypeToJsonType(field_ControlType),
622
+ title: field_LabelText,
623
+ description: helpText,
624
+ required: (field_ValidationRequired === null || field_ValidationRequired === void 0 ? void 0 : field_ValidationRequired.toLowerCase()) === 'required',
625
+ default: defaultValue || undefined,
626
+ realType: field_ControlType,
627
+ Enabled: isEnabled,
628
+ ID: field_ID,
629
+ TFie_PK_ID: field_TFie_PK_ID,
630
+ TVal_PK_ID: field_TVal_PK_ID,
631
+ TFIn_PK_ID: field_TFIn_PK_ID,
632
+ isForm: field_isForm,
633
+ LabelText: field_LabelText,
634
+ SystemName: field_SystemName,
635
+ CssStyle: field_CssStyle,
636
+ ControlType: field_ControlType,
637
+ ValidationRequired: field_ValidationRequired,
638
+ ValidationMin: field_ValidationMin,
639
+ ValidationMax: field_ValidationMax,
640
+ Mask: field_Mask,
641
+ TypeId: field_TypeId,
642
+ Unit: field_Unit,
643
+ TFie_Fullpath: field_TFie_Fullpath,
644
+ TVal_FK_Parent_ID: field_TVal_FK_Parent_ID
645
+ };
646
+ // Handle select options
647
+ if (field.type.toLowerCase() === 'select') {
648
+ try {
649
+ const response = await fetch(`${classificationRootURL}/api/v1/classification/getList/${field.ControlType.toLowerCase() == 'termpicker' ? "position" : "thesaurus"}/?StartNodeID=${field_TypeId}&Language=${lang}`);
650
+ if (!response.ok)
651
+ throw new Error(`HTTP error! status: ${response.status}`);
652
+ const data = await response.json();
653
+ if (Array.isArray(data)) {
654
+ const options = data.map(item => ({
655
+ value: item.ID,
656
+ label: item.System_Name
657
+ }));
658
+ if (options.length > 0) {
659
+ field.pickerOptions = options;
660
+ // TODO on degage les quotes simples temporairement, a corriger
661
+ field.options = options.map(option => option.label.replace("'", ""));
662
+ }
663
+ }
664
+ else {
665
+ console.error("Unexpected API response format:", data);
666
+ }
667
+ }
668
+ catch (error) {
669
+ console.error("Error fetching classification data:", error);
670
+ }
671
+ }
672
+ if (field_ValidationMin)
673
+ field.min = parseFloat(field_ValidationMin);
674
+ if (field_ValidationMax)
675
+ field.max = parseFloat(field_ValidationMax);
676
+ if (field_Mask)
677
+ field.pattern = field_Mask;
678
+ // Optional: Add any other fields you want to track, e.g. min, max, pattern (not clearly in your XML)
679
+ // You could also add the raw ControlType for round-trip fidelity if you want
680
+ schema.fields[field_ID] = field;
681
+ }
682
+ return schema;
683
+ }
684
+ catch (error) {
685
+ throw new Error(`XML to JSON conversion failed: ${error.message}`);
686
+ }
687
+ }
688
+ /**
689
+ * Convert JSON schema back to your new XML form definition format
690
+ */
691
+ static convertJsonToXml(jsonForm) {
692
+ try {
693
+ const doc = document.implementation.createDocument('', '', null);
694
+ const xforms = doc.createElement('Xforms');
695
+ Object.entries((jsonForm.schema ? jsonForm.schema : jsonForm.fields)).forEach(([fieldID, field]) => {
696
+ const fieldEl = doc.createElement('Fields');
697
+ const fieldToUse = field;
698
+ // Set all subelements based on your XML structure
699
+ // ID, PK IDs etc are missing here, so omit or generate dummy
700
+ // <SystemName>
701
+ const systemNameEl = doc.createElement('SystemName');
702
+ systemNameEl.textContent = fieldToUse.SystemName;
703
+ fieldEl.appendChild(systemNameEl);
704
+ // <LabelText>
705
+ const labelEl = doc.createElement('LabelText');
706
+ labelEl.textContent = fieldToUse.title || fieldID;
707
+ fieldEl.appendChild(labelEl);
708
+ // <HelpText>
709
+ const helpEl = doc.createElement('HelpText');
710
+ helpEl.textContent = fieldToUse.description;
711
+ fieldEl.appendChild(helpEl);
712
+ // <ControlType>
713
+ const controlTypeEl = doc.createElement('ControlType');
714
+ controlTypeEl.textContent = fieldToUse.realType;
715
+ fieldEl.appendChild(controlTypeEl);
716
+ // <ValidationRequired>
717
+ const validationEl = doc.createElement('ValidationRequired');
718
+ validationEl.textContent = fieldToUse.required ? 'Required' : 'Not Required';
719
+ fieldEl.appendChild(validationEl);
720
+ // <DefaultValue>
721
+ const defaultEl = doc.createElement('DefaultValue');
722
+ const valueEl = doc.createElement('Value');
723
+ if (jsonForm[fieldID] !== undefined) {
724
+ defaultEl.textContent = jsonForm[fieldID].toString();
725
+ valueEl.textContent = jsonForm[fieldID].toString();
726
+ }
727
+ else {
728
+ defaultEl.textContent = fieldToUse.default || fieldToUse.value;
729
+ valueEl.textContent = fieldToUse.value || fieldToUse.default;
730
+ }
731
+ fieldEl.appendChild(defaultEl);
732
+ fieldEl.appendChild(valueEl);
733
+ // <Enabled>
734
+ const enabledEl = doc.createElement('Enabled');
735
+ enabledEl.textContent = fieldToUse.Enabled === false ? 'False' : 'True';
736
+ fieldEl.appendChild(enabledEl);
737
+ // Add <isForm> Field to distinguish Field or Header etc
738
+ const isFormEl = doc.createElement('isForm');
739
+ isFormEl.textContent = fieldToUse.isForm;
740
+ fieldEl.appendChild(isFormEl);
741
+ if (fieldToUse.type === 'header') {
742
+ const headerEl = doc.createElement('Header');
743
+ headerEl.textContent = fieldToUse.title;
744
+ fieldEl.appendChild(headerEl);
745
+ }
746
+ const idEl = doc.createElement('ID');
747
+ idEl.textContent = fieldToUse.ID;
748
+ fieldEl.appendChild(idEl);
749
+ const tfiePkIdEl = doc.createElement('TFie_PK_ID');
750
+ tfiePkIdEl.textContent = fieldToUse.TFie_PK_ID;
751
+ fieldEl.appendChild(tfiePkIdEl);
752
+ const tvalPkIdEl = doc.createElement('TVal_PK_ID');
753
+ tvalPkIdEl.textContent = fieldToUse.TVal_PK_ID;
754
+ fieldEl.appendChild(tvalPkIdEl);
755
+ const tfInPkIdEl = doc.createElement('TFIn_PK_ID');
756
+ tfInPkIdEl.textContent = fieldToUse.TFIn_PK_ID;
757
+ fieldEl.appendChild(tfInPkIdEl);
758
+ const cssStyleEl = doc.createElement('CssStyle');
759
+ cssStyleEl.textContent = fieldToUse.CssStyle;
760
+ fieldEl.appendChild(cssStyleEl);
761
+ const typeIdEl = doc.createElement('TypeId');
762
+ typeIdEl.textContent = fieldToUse.TypeId;
763
+ fieldEl.appendChild(typeIdEl);
764
+ const unitEl = doc.createElement('Unit');
765
+ unitEl.textContent = fieldToUse.Unit;
766
+ fieldEl.appendChild(unitEl);
767
+ const tfieFullPathEl = doc.createElement('TFie_Fullpath');
768
+ tfieFullPathEl.textContent = fieldToUse.TFie_Fullpath;
769
+ fieldEl.appendChild(tfieFullPathEl);
770
+ const tvalFkParentIdEl = doc.createElement('TVal_FK_Parent_ID');
771
+ tvalFkParentIdEl.textContent = fieldToUse.TVal_FK_Parent_ID;
772
+ fieldEl.appendChild(tvalFkParentIdEl);
773
+ const validationMinEl = doc.createElement('ValidationMin');
774
+ validationMinEl.textContent = fieldToUse.ValidationMin;
775
+ fieldEl.appendChild(validationMinEl);
776
+ const validationMaxEl = doc.createElement('ValidationMax');
777
+ validationMaxEl.textContent = fieldToUse.ValidationMax;
778
+ fieldEl.appendChild(validationMaxEl);
779
+ const maskEl = doc.createElement('Mask');
780
+ maskEl.textContent = fieldToUse.Mask;
781
+ fieldEl.appendChild(maskEl);
782
+ // Append to Xforms root
783
+ xforms.appendChild(fieldEl);
784
+ });
785
+ doc.appendChild(xforms);
786
+ const serializer = new XMLSerializer();
787
+ return serializer.serializeToString(doc);
788
+ }
789
+ catch (error) {
790
+ throw new Error(`JSON to XML conversion failed: ${error.message}`);
791
+ }
792
+ }
793
+ static mapControlTypeToJsonType(controlType) {
794
+ // Map your ControlType XML field to JSON field types
795
+ switch (controlType.toLowerCase()) {
796
+ case 'textbox':
797
+ case 'text':
798
+ return 'string';
799
+ case 'datepicker':
800
+ return 'date';
801
+ case 'thesauruspicker':
802
+ case 'thesauruspicker-ddl':
803
+ case 'termpicker':
804
+ return 'select';
805
+ case 'dbpicker':
806
+ return 'dbpicker'; // custom type if needed
807
+ case 'header':
808
+ return 'header'; // special type to handle header elements
809
+ case 'checkbox':
810
+ return 'boolean';
811
+ case 'realpicker':
812
+ return 'number';
813
+ default:
814
+ return 'string';
815
+ }
816
+ }
817
+ }
818
+
819
+ const voiceInputModuleCss = ":host{display:block;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif}.voice-recorder-container{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1rem;border:1px solid #e5e7eb;border-radius:0.5rem;background:#ffffff;max-width:100px;margin:0 auto}.voice-recorder-container-debug{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1rem;border:1px solid #e5e7eb;border-radius:0.5rem;background:#ffffff;max-width:800px;margin:0 auto}.record-button{display:flex;align-items:center;justify-content:center;width:50px;height:50px;border:none;border-radius:50%;background:#ee4444;color:white;cursor:pointer;transition:all 0.2s ease;position:relative;overflow:hidden}.record-button:hover:not(:disabled){background:#dd3333;transform:scale(1.05)}.record-button:disabled{opacity:0.6;cursor:not-allowed}.record-button.recording{background:#dd3333;animation:pulse 1.5s infinite}.record-button.processing{background:#3b82f6}@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(239, 68, 68, 0.7)}70%{box-shadow:0 0 0 10px rgba(239, 68, 68, 0)}100%{box-shadow:0 0 0 0 rgba(239, 68, 68, 0)}}.record-icon{width:24px;height:24px;fill:currentColor}.status-text{font-size:0.875rem;color:#6b7280;text-align:center;min-height:1.25rem}.status-text.error{color:#ee4444}.status-text.success{color:#10b981}.debug-panel{width:100%;margin-top:1rem;padding:1rem;background:#f9fafb;border:1px solid #e5e7eb;border-radius:0.375rem;font-family:'Monaco', 'Menlo', 'Ubuntu Mono', monospace;font-size:0.75rem}.debug-title{font-weight:600;margin-bottom:0.5rem;color:#374151}.debug-content{white-space:pre-wrap;word-break:break-word;color:#6b7280;max-height:200px;overflow-y:auto}.permissions-warning{padding:0.75rem;background:#fef3c7;border:1px solid #f59e0b;border-radius:0.375rem;color:#92400e;font-size:0.875rem;text-align:center}.form-preview{width:100%;margin-top:1rem;padding:1rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:0.375rem}.form-preview-title{font-weight:600;margin-bottom:0.5rem;color:#1e293b}.form-field{margin-bottom:0.5rem;font-size:0.875rem}.field-name{font-weight:500;color:#475569}.field-value{color:#64748b;margin-left:0.5rem}.field-value.filled{color:#059669;font-weight:500}.voice-filled-form{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.form-group{display:flex;flex-direction:column;gap:0.25rem}.form-label{font-weight:500;color:#374151;font-size:0.875rem}.required{color:#ee4444;margin-left:0.125rem}.form-input{padding:0.5rem;border:1px solid #d1d5db;border-radius:0.375rem;font-size:0.875rem;transition:border-color 0.2s ease}.form-input:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.form-checkbox{width:auto;padding:0;margin:0}select.form-input{background-color:white;cursor:pointer}input[type=\"date\"].form-input{cursor:pointer}.readonly-select{background:#f9fafb;border:1px solid #d1d5db;border-radius:0.375rem;padding:0.5rem}.select-placeholder{font-size:0.875rem;color:#6b7280;margin-bottom:0.5rem}.select-options-list{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:0.25rem}.select-option{background:#e5e7eb;padding:0.25rem 0.5rem;border-radius:0.25rem;font-size:0.875rem;color:#374151}";
820
+
821
+ const VoiceFormRecorder = class {
822
+ constructor(hostRef) {
823
+ registerInstance(this, hostRef);
824
+ this.formFilled = createEvent(this, "formFilled");
825
+ this.recordingStateChanged = createEvent(this, "recordingStateChanged");
826
+ this.formJson = '{}';
827
+ this.serviceConfig = '{}';
828
+ this.context = undefined;
829
+ this.classificationRootUrl = 'http://localhost';
830
+ this.language = 'en';
831
+ this.voiceOrOcr = undefined;
832
+ this.debug = false;
833
+ this.renderForm = false;
834
+ this.displayStatus = false;
835
+ this.isRecording = false;
836
+ this.isProcessing = false;
837
+ this.hasError = false;
838
+ this.transcription = '';
839
+ this.filledData = null;
840
+ this.debugInfo = {};
841
+ this.isReadonlyMode = true; // Start in readonly preview mode
842
+ this.audioRecorder = new AudioRecorderService();
843
+ }
844
+ componentWillLoad() {
845
+ this.initializeServices();
846
+ }
847
+ initializeServices() {
848
+ try {
849
+ if (!this.context) {
850
+ this.hasError = true;
851
+ const errorMessage = (this.language == 'en' ? `Initialization error: context is '${this.context}'` : `Erreur d'initialisation: le contexte est '${this.context}'`);
852
+ this.statusMessage = errorMessage;
853
+ this.updateDebugInfo(errorMessage, { error: errorMessage });
854
+ }
855
+ else {
856
+ // Parse form schema
857
+ this.parsedSchema = JSON.parse(this.formJson || '{}');
858
+ // Parse service configuration
859
+ this.parsedConfig = JSON.parse(this.serviceConfig || '{}');
860
+ // Add API key to config if provided via prop
861
+ if (this.apiKey) {
862
+ this.parsedConfig = Object.assign(Object.assign({}, this.parsedConfig), { speechToText: Object.assign(Object.assign({}, this.parsedConfig.speechToText), { apiKey: this.apiKey }), llm: Object.assign(Object.assign({}, this.parsedConfig.llm), { apiKey: this.apiKey }) });
863
+ }
864
+ // Initialize services
865
+ this.speechToTextService = SpeechToTextServiceFactory.create(this.parsedConfig);
866
+ this.llmService = LLMServiceFactory.create(this.parsedConfig);
867
+ this.updateDebugInfo('Initialized', {
868
+ schema: this.parsedSchema,
869
+ config: this.parsedConfig
870
+ });
871
+ this.hasError = false;
872
+ this.statusMessage = (this.language == 'en' ? 'Click to start recording' : 'Cliquer pour enregistrer');
873
+ }
874
+ }
875
+ catch (error) {
876
+ this.hasError = true;
877
+ this.statusMessage = (this.language == 'en' ? `Initialization error: ${error.message}` : `Erreur d'initialisation: ${error.message}`);
878
+ this.updateDebugInfo('Initialization Error', { error: error.message });
879
+ }
880
+ }
881
+ updateDebugInfo(action, data) {
882
+ if (this.debug) {
883
+ this.debugInfo = Object.assign(Object.assign({}, this.debugInfo), { [action]: {
884
+ timestamp: new Date().toISOString(),
885
+ data
886
+ } });
887
+ }
888
+ }
889
+ async handleRecordClick() {
890
+ if (this.isProcessing)
891
+ return;
892
+ if (this.isRecording) {
893
+ await this.stopRecordingAndProcess();
894
+ }
895
+ else {
896
+ await this.startRecording();
897
+ }
898
+ }
899
+ async startRecording() {
900
+ try {
901
+ this.hasError = false;
902
+ this.statusMessage = (this.language == 'en' ? 'Starting recording...' : `Enregistrement ...`);
903
+ this.updateDebugInfo('Start Recording Attempt', {});
904
+ await this.audioRecorder.startRecording();
905
+ this.isRecording = true;
906
+ this.statusMessage = (this.language == 'en' ? 'Recording... Click to stop' : 'Enregistrement ... Cliquer pour stopper');
907
+ this.updateDebugInfo('Recording Started', {});
908
+ this.recordingStateChanged.emit({
909
+ isRecording: true,
910
+ state: 'recording'
911
+ });
912
+ }
913
+ catch (error) {
914
+ this.hasError = true;
915
+ this.statusMessage = (this.language == 'en' ? `Recording failed: ${error.message}` : `Echec de l'enregistrement : ${error.message}`);
916
+ this.updateDebugInfo('Recording Error', { error: error.message });
917
+ this.formFilled.emit({
918
+ success: false,
919
+ error: error.message
920
+ });
921
+ }
922
+ }
923
+ async processJsonForm(jsonForm) {
924
+ // console.log("processJsonForm", jsonForm);
925
+ try {
926
+ this.isProcessing = true;
927
+ this.statusMessage = (this.language == 'en' ? 'Processing json...' : `Traitement du json ...`);
928
+ // Fill form using LLM
929
+ this.statusMessage = (this.language == 'en' ? 'Filling form fields...' : 'Remplissage du formulaire ...');
930
+ const trimmedSchema = this.trimSchemaForAI(this.parsedSchema);
931
+ const filledSchema = await this.llmService.fillFormFromJson(jsonForm, trimmedSchema);
932
+ // Extract filled data
933
+ this.filledData = this.extractFilledData(filledSchema);
934
+ this.updateDebugInfo('Form Filled', {
935
+ filledSchema,
936
+ extractedData: this.filledData
937
+ });
938
+ this.parsedSchema = this.filledData;
939
+ this.statusMessage = (this.language == 'en' ? 'Form completed!' : 'Formulaire remplis !');
940
+ this.hasError = false;
941
+ // Emit success event
942
+ this.formFilled.emit({
943
+ success: true,
944
+ data: this.filledData,
945
+ jsonForm: jsonForm
946
+ });
947
+ }
948
+ catch (error) {
949
+ this.hasError = true;
950
+ this.statusMessage = (this.language == 'en' ? `Processing failed: ${error.message}` : `Erreur de traitement : ${error.message}`);
951
+ this.formFilled.emit({
952
+ success: false,
953
+ error: error.message,
954
+ jsonForm: jsonForm
955
+ });
956
+ }
957
+ finally {
958
+ this.isProcessing = false;
959
+ }
960
+ }
961
+ async stopRecordingAndProcess() {
962
+ try {
963
+ this.isRecording = false;
964
+ this.isProcessing = true;
965
+ this.statusMessage = (this.language == 'en' ? 'Processing audio...' : `Traitement de l'audio ...`);
966
+ this.updateDebugInfo('Stop Recording', {});
967
+ this.recordingStateChanged.emit({
968
+ isRecording: false,
969
+ state: 'processing'
970
+ });
971
+ // Stop recording and get audio blob
972
+ const audioBlob = await this.audioRecorder.stopRecording();
973
+ this.updateDebugInfo('Audio Captured', {
974
+ size: audioBlob.size,
975
+ type: audioBlob.type
976
+ });
977
+ // Transcribe audio
978
+ this.statusMessage = (this.language == 'en' ? 'Transcribing speech...' : 'Transcription du texte ...');
979
+ const transcription = await this.speechToTextService.transcribe(audioBlob, this.language);
980
+ this.transcription = transcription;
981
+ this.updateDebugInfo('Transcription Complete', { transcription });
982
+ if (!transcription.trim()) {
983
+ throw new Error('No speech detected in the recording');
984
+ }
985
+ // Fill form using LLM
986
+ this.statusMessage = (this.language == 'en' ? 'Filling form fields...' : 'Remplissage du formulaire ...');
987
+ const trimmedSchema = this.trimSchemaForAI(this.parsedSchema);
988
+ const filledSchema = await this.llmService.fillFormFromTranscription(transcription, trimmedSchema);
989
+ // Extract filled data
990
+ this.filledData = this.extractFilledData(filledSchema);
991
+ this.updateDebugInfo('Form Filled', {
992
+ filledSchema,
993
+ extractedData: this.filledData
994
+ });
995
+ this.parsedSchema = this.filledData;
996
+ this.statusMessage = (this.language == 'en' ? 'Form completed!' : 'Formulaire remplis !');
997
+ this.hasError = false;
998
+ // Emit success event
999
+ this.formFilled.emit({
1000
+ success: true,
1001
+ data: this.filledData,
1002
+ transcription: transcription
1003
+ });
1004
+ }
1005
+ catch (error) {
1006
+ this.hasError = true;
1007
+ this.statusMessage = (this.language == 'en' ? `Processing failed: ${error.message}` : `Erreur de traitement : ${error.message}`);
1008
+ this.updateDebugInfo('Processing Error', { error: error.message });
1009
+ this.formFilled.emit({
1010
+ success: false,
1011
+ error: error.message,
1012
+ transcription: this.transcription
1013
+ });
1014
+ }
1015
+ finally {
1016
+ this.isProcessing = false;
1017
+ this.recordingStateChanged.emit({
1018
+ isRecording: false,
1019
+ state: 'idle'
1020
+ });
1021
+ }
1022
+ }
1023
+ extractFilledData(filledData) {
1024
+ // console.log("extractFilledData", filledData);
1025
+ const updatedSchema = JSON.parse(JSON.stringify(this.parsedSchema));
1026
+ switch (this.context) {
1027
+ case "ng":
1028
+ if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
1029
+ // Map AI response back to original schema structure
1030
+ filledData.fields.forEach((field) => {
1031
+ const originalField = updatedSchema.Children.find((child) => child.System_Name === field.name);
1032
+ if (originalField && field.value !== undefined && field.value !== null && field.value !== '') {
1033
+ if (!originalField.Settings)
1034
+ originalField.Settings = {};
1035
+ originalField.Settings.Default_Value = field.value;
1036
+ }
1037
+ });
1038
+ }
1039
+ break;
1040
+ case "ecoll-veto":
1041
+ // console.log("TODO extractFilledData", filledData, updatedSchema);
1042
+ if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
1043
+ // Map AI response back to original schema structure
1044
+ filledData.fields.forEach((field) => {
1045
+ let originalField = updatedSchema[0].items.find((child) => child.label === field.name);
1046
+ if (!originalField)
1047
+ originalField = updatedSchema[1].items.find((child) => child.label === field.name);
1048
+ if (originalField && field.value !== undefined && field.value !== null && field.value !== '') {
1049
+ updatedSchema[2][originalField.name] = field.value;
1050
+ }
1051
+ });
1052
+ }
1053
+ break;
1054
+ case "ecoteka":
1055
+ // console.log("TODO extractFilledData", filledData);
1056
+ break;
1057
+ case "track":
1058
+ default:
1059
+ Object.entries(filledData.fields).forEach(([fieldID, field]) => {
1060
+ if (field.default !== undefined && field.default !== null && field.default !== '') {
1061
+ updatedSchema[fieldID] = field.default;
1062
+ }
1063
+ if (field.value !== undefined && field.value !== null && field.value !== '') {
1064
+ for (const key in updatedSchema.fields) {
1065
+ const schemaField = updatedSchema.fields[key];
1066
+ if (schemaField.title === field.name) {
1067
+ schemaField.value = field.value || field.default;
1068
+ schemaField.default = field.value || field.default;
1069
+ break; // stop after finding the first match
1070
+ }
1071
+ }
1072
+ }
1073
+ });
1074
+ break;
1075
+ }
1076
+ // console.log("extractFilledData result", updatedSchema);
1077
+ return updatedSchema;
1078
+ }
1079
+ trimSchemaForAI(schema) {
1080
+ var _a, _b;
1081
+ // console.log("trimSchemaForAI", schema);
1082
+ switch (this.context) {
1083
+ case 'ng':
1084
+ const trimmed = { fields: [] };
1085
+ schema.Children.forEach((child) => {
1086
+ if (!child.System_Name || !child.Type)
1087
+ return;
1088
+ const fieldData = {
1089
+ name: child.System_Name,
1090
+ type: this.mapFieldType(child.Type)
1091
+ };
1092
+ // Add options for classification/select fields
1093
+ if (child.Type === 'InputClassification' && child.Children && child.Children.length > 0) {
1094
+ fieldData.options = child.Children.map((option) => option.System_Name || option.Label || option.toString());
1095
+ }
1096
+ trimmed.fields.push(fieldData);
1097
+ });
1098
+ // console.log("Schema apres transformation, contexte NG:", trimmed);
1099
+ return trimmed;
1100
+ case 'ecoll-veto':
1101
+ // console.log("TODO trimSchemaForAI", schema)
1102
+ const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
1103
+ if (mergedItemsSchema) {
1104
+ const trimmedSchema = {
1105
+ title: 'Form Name',
1106
+ description: 'Form Description',
1107
+ schema: {}
1108
+ };
1109
+ Object.entries(mergedItemsSchema).forEach(([key, field]) => {
1110
+ const fieldName = field.name;
1111
+ const fieldType = this.mapFieldType(field.type);
1112
+ const fieldLabel = field.label || fieldName;
1113
+ trimmedSchema.schema[fieldName] = {
1114
+ type: fieldType,
1115
+ title: fieldLabel,
1116
+ options: field.options,
1117
+ readonly: field.readonly === true,
1118
+ default: '',
1119
+ };
1120
+ });
1121
+ // console.log("Schema apres transformation, contexte Track:", trimmedSchema);
1122
+ return trimmedSchema;
1123
+ }
1124
+ case "ecoteka":
1125
+ // console.log("TODO trimSchemaForAI", schema)
1126
+ break;
1127
+ case 'track':
1128
+ default:
1129
+ // Handle simple schema format (backward compatibility)
1130
+ if ((_a = schema === null || schema === void 0 ? void 0 : schema.schema) !== null && _a !== void 0 ? _a : schema === null || schema === void 0 ? void 0 : schema.fields) {
1131
+ const trimmedSchema = {
1132
+ title: schema.title,
1133
+ description: schema.description,
1134
+ schema: {}
1135
+ };
1136
+ const finalSchema = (_b = schema === null || schema === void 0 ? void 0 : schema.schema) !== null && _b !== void 0 ? _b : schema === null || schema === void 0 ? void 0 : schema.fields;
1137
+ Object.entries(finalSchema).forEach(([fieldName, field]) => {
1138
+ trimmedSchema.schema[fieldName] = {
1139
+ type: field.type,
1140
+ title: field.title,
1141
+ options: field.options,
1142
+ readonly: field.Enabled === false,
1143
+ default: field.DefaultValue,
1144
+ pattern: field.Mask,
1145
+ min: field.ValidationMin,
1146
+ max: field.ValidationMax
1147
+ };
1148
+ });
1149
+ // console.log("Schema apres transformation, contexte Track:", trimmedSchema);
1150
+ return trimmedSchema;
1151
+ }
1152
+ break;
1153
+ }
1154
+ return schema;
1155
+ }
1156
+ mapFieldType(type) {
1157
+ const typeMapping = {
1158
+ 'InputAutocomplete': 'string',
1159
+ 'InputInteger': 'number',
1160
+ 'InputTextArea': 'string',
1161
+ 'InputDateTimePicker': 'datetime',
1162
+ 'InputDecimal': 'number',
1163
+ 'InputClassification': 'select',
1164
+ 'InputCheckbox': 'boolean',
1165
+ 'InputTextTranslation': 'string',
1166
+ 'thesaurus': 'string',
1167
+ 'position': 'string',
1168
+ 'text': 'string',
1169
+ 'textarea': 'string',
1170
+ 'number': 'number',
1171
+ 'date': 'date',
1172
+ 'datetime': 'datetime'
1173
+ };
1174
+ return typeMapping[type] || 'string';
1175
+ }
1176
+ // Utility methods exposed as public API
1177
+ async convertXmlToJson(xmlForm) {
1178
+ return SchemaConverter.convertXmlToJson(xmlForm, this.classificationRootUrl, this.language);
1179
+ }
1180
+ async convertJsonToXml(jsonForm) {
1181
+ return SchemaConverter.convertJsonToXml(jsonForm);
1182
+ }
1183
+ // Utility methods exposed as public API
1184
+ async convertXmlToJsonLegacy(xmlForm) {
1185
+ return SchemaConverter.convertXmlToJsonLegacy(xmlForm);
1186
+ }
1187
+ async convertJsonToXmlLegacy(jsonForm) {
1188
+ return SchemaConverter.convertJsonToXmlLegacy(jsonForm);
1189
+ }
1190
+ renderUploadButton() {
1191
+ if (!['ocr', 'both'].includes(this.voiceOrOcr))
1192
+ return;
1193
+ return (h("file-uploader", { batch: false, callback: (data) => { this.processJsonForm(data); } }));
1194
+ }
1195
+ renderRecordButton() {
1196
+ if (!['voice', 'both', undefined].includes(this.voiceOrOcr))
1197
+ return;
1198
+ const buttonClass = [
1199
+ 'record-button',
1200
+ this.isRecording && 'recording',
1201
+ this.isProcessing && 'processing'
1202
+ ].filter(Boolean).join(' ');
1203
+ const isDisabled = this.isProcessing || this.hasError;
1204
+ return (h("button", { class: buttonClass, onClick: () => this.handleRecordClick(), disabled: isDisabled, "aria-label": this.isRecording ? 'Stop recording' : 'Start recording' }, this.isProcessing ? (h("svg", { class: "record-icon", viewBox: "0 0 24 24" }, h("circle", { cx: "12", cy: "12", r: "3" }, h("animate", { attributeName: "r", values: "3;6;3", dur: "1s", repeatCount: "indefinite" }), h("animate", { attributeName: "opacity", values: "1;0.3;1", dur: "1s", repeatCount: "indefinite" })))) : this.isRecording ? (h("svg", { class: "record-icon", viewBox: "0 0 24 24" }, h("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }))) : (h("svg", { class: "record-icon", viewBox: "0 0 24 24" }, h("circle", { cx: "12", cy: "12", r: "8" })))));
1205
+ }
1206
+ renderStatusMessage() {
1207
+ const statusClass = [
1208
+ 'status-text',
1209
+ this.hasError && 'error',
1210
+ this.filledData && !this.hasError && 'success'
1211
+ ].filter(Boolean).join(' ');
1212
+ return h("div", { class: statusClass }, this.statusMessage);
1213
+ }
1214
+ renderFormPreview() {
1215
+ if (!this.parsedSchema)
1216
+ return null;
1217
+ const isPreview = this.isReadonlyMode && !this.filledData;
1218
+ const title = isPreview ? 'Form Preview (Voice input to fill)' : 'Voice-Filled Form:';
1219
+ return (h("div", { class: "form-preview" }, h("div", { class: "form-preview-title" }, title), h("form", { class: "voice-filled-form" }, this.renderFormFields())));
1220
+ }
1221
+ renderFormFields() {
1222
+ // console.log("renderFormFields", this.parsedSchema);
1223
+ if (!this.parsedSchema)
1224
+ return null;
1225
+ switch (this.context) {
1226
+ case "ng":
1227
+ return this.parsedSchema.Children.map((child) => {
1228
+ var _a, _b, _c;
1229
+ if (!child.System_Name || !child.Type)
1230
+ return null;
1231
+ const fieldName = child.System_Name;
1232
+ const fieldType = this.mapFieldType(child.Type);
1233
+ const fieldLabel = ((_a = child.Settings) === null || _a === void 0 ? void 0 : _a.Label) || child.System_Name;
1234
+ const isRequired = child.Required || false;
1235
+ const fieldValue = (_b = child.Settings) === null || _b === void 0 ? void 0 : _b.Default_Value;
1236
+ return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h("span", { class: "required" }, "*")), this.renderFormField(fieldName, {
1237
+ type: fieldType,
1238
+ title: fieldLabel,
1239
+ required: isRequired,
1240
+ options: (_c = child.Children) === null || _c === void 0 ? void 0 : _c.map((option) => option.System_Name || option.Label || option.toString())
1241
+ }, fieldValue)));
1242
+ }).filter(Boolean);
1243
+ case "ecoll-veto":
1244
+ // NOTE STEP 2
1245
+ // console.log("TODO renderFormFields", this.parsedSchema);
1246
+ const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
1247
+ return Object.entries(mergedItemsSchema).map(([key, field]) => {
1248
+ var _a, _b;
1249
+ const fieldName = field.name;
1250
+ field.type = this.mapFieldType(field.type);
1251
+ const fieldLabel = field.label || fieldName;
1252
+ const isRequired = field.required || false;
1253
+ const fieldValue = this.parsedSchema[2][fieldName];
1254
+ return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h("span", { class: "required" }, "*")), this.renderFormField(fieldName, field, ((_b = (_a = this.filledData) === null || _a === void 0 ? void 0 : _a[fieldName]) !== null && _b !== void 0 ? _b : fieldValue))));
1255
+ });
1256
+ case "ecoteka":
1257
+ // console.log("TODO renderFormFields", this.parsedSchema);
1258
+ break;
1259
+ case "track":
1260
+ default:
1261
+ return Object.entries(this.parsedSchema.fields).map(([fieldName, field]) => {
1262
+ var _a, _b;
1263
+ return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && h("span", { class: "required" }, "*")), this.renderFormField(fieldName, field, ((_b = (_a = this.filledData) === null || _a === void 0 ? void 0 : _a[fieldName]) !== null && _b !== void 0 ? _b : field.value))));
1264
+ });
1265
+ }
1266
+ return null;
1267
+ }
1268
+ renderFormField(fieldName, field, value) {
1269
+ var _a, _b;
1270
+ const isReadonly = this.isReadonlyMode && !this.filledData;
1271
+ const commonProps = {
1272
+ id: fieldName,
1273
+ name: fieldName,
1274
+ class: 'form-input',
1275
+ required: field.required,
1276
+ disabled: isReadonly
1277
+ };
1278
+ switch (field.type) {
1279
+ case 'select':
1280
+ if (isReadonly) {
1281
+ // In readonly mode, show all options as a list instead of dropdown
1282
+ return (h("div", { class: "readonly-select" }, h("div", { class: "select-placeholder" }, "Available options:"), h("ul", { class: "select-options-list" }, (_a = field.options) === null || _a === void 0 ? void 0 : _a.map(option => (h("li", { class: "select-option" }, option))))));
1283
+ }
1284
+ return (h("select", { id: fieldName, name: fieldName, class: "form-input", required: field.required }, h("option", { value: "" }, "-- Select --"), (_b = field.options) === null || _b === void 0 ? void 0 :
1285
+ _b.map(option => (h("option", { value: option, selected: value === option }, option)))));
1286
+ case 'boolean':
1287
+ return (h("input", Object.assign({}, commonProps, { type: "checkbox", class: "form-checkbox", checked: value === true || value === 'true' })));
1288
+ case 'number':
1289
+ return (h("input", Object.assign({}, commonProps, { type: "number", min: field.min, max: field.max, step: "any", value: value || '' })));
1290
+ case 'date':
1291
+ return (h("input", Object.assign({}, commonProps, { type: 'date', value: value ? (([d, m, y]) => `${y}-${m}-${d}`)(value.split("/")) : '' })));
1292
+ case 'datetime':
1293
+ return (h("input", Object.assign({}, commonProps, { type: 'datetime-local', value: value || '' })));
1294
+ default: // string
1295
+ return (h("input", Object.assign({}, commonProps, { type: "text", pattern: field.pattern, placeholder: field.description, value: value || '' })));
1296
+ }
1297
+ }
1298
+ renderDebugPanel() {
1299
+ if (!this.debug)
1300
+ return null;
1301
+ return (h("div", { class: "debug-panel" }, h("div", { class: "debug-title" }, "Debug Information:"), h("div", { class: "debug-content" }, JSON.stringify(this.debugInfo, null, 2))));
1302
+ }
1303
+ render() {
1304
+ return (h("div", { key: '40d850c62945b71d22c1fcb6181c9e8aba0f2a05' }, h("div", { key: 'da8aa5b7f122471a8d4d49662d418bd925f1d84c', class: "voice-recorder-container" + (this.debug || this.renderForm ? "-debug" : "") }, this.renderRecordButton(), this.displayStatus ? this.renderStatusMessage() : "", this.renderUploadButton(), this.renderForm ? this.renderFormPreview() : "", this.debug ? this.renderDebugPanel() : "")));
1305
+ }
1306
+ static get watchers() { return {
1307
+ "formJson": ["initializeServices"],
1308
+ "serviceConfig": ["initializeServices"]
1309
+ }; }
1310
+ };
1311
+ VoiceFormRecorder.style = voiceInputModuleCss;
1312
+
1313
+ export { FileUploader as file_uploader, VoiceFormRecorder as voice_input_module };
1314
+ //# sourceMappingURL=file-uploader.voice-input-module.entry.js.map
1315
+
1316
+ //# sourceMappingURL=file-uploader_2.entry.js.map