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