ai-form-fill 0.1.0

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.
@@ -0,0 +1,715 @@
1
+ let l = {
2
+ ollama: {
3
+ apiEndpoint: "http://localhost:11434",
4
+ model: "gemma3:4b"
5
+ },
6
+ openai: {
7
+ apiEndpoint: "http://localhost:5173/api",
8
+ // http://localhost:5173/api for local testing proxy
9
+ model: "gpt-5-nano"
10
+ },
11
+ perplexity: {
12
+ apiEndpoint: "http://localhost:5173/api",
13
+ // http://localhost:5173/api for local testing proxy
14
+ model: "sonar"
15
+ },
16
+ providerDebug: !0,
17
+ formFillDebug: !0,
18
+ timeout: 3e4
19
+ };
20
+ class y {
21
+ /**
22
+ * **Optional**: Concrete link to endpoint that sends chat messages
23
+ */
24
+ chatEndpoint;
25
+ /**
26
+ * **Optional**: Concrete link to endpoint that lists available models
27
+ */
28
+ listModelsEndpoint;
29
+ /**
30
+ * **Optional**: Concrete link to endpoint that checks API availability
31
+ */
32
+ availabilityEndpoint;
33
+ selectedModel;
34
+ apiEndpoint;
35
+ timeout;
36
+ supportsStructuredResponses = !1;
37
+ constructor(e) {
38
+ this.apiEndpoint = e?.apiEndpoint || "", this.selectedModel = e?.model || "", this.timeout = e?.timeout || 3e4;
39
+ }
40
+ /** Returns the currently selected model. */
41
+ getSelectedModel() {
42
+ return this.selectedModel;
43
+ }
44
+ /**
45
+ * Sets the model to use for chat requests. Validates against available models if possible.
46
+ */
47
+ async setSelectedModel(e) {
48
+ if (!e) return !1;
49
+ try {
50
+ const t = await this.listModels();
51
+ return t && t.includes(e) ? (this.selectedModel = e, !0) : (l.providerDebug && console.warn(`Model "${e}" not found. Available: ${t.join(", ")}`), !1);
52
+ } catch (t) {
53
+ return l.providerDebug && console.warn("Could not validate model:", t), this.selectedModel = e, !0;
54
+ }
55
+ }
56
+ getName() {
57
+ return this.providerName;
58
+ }
59
+ /**
60
+ * Indicates if the provider supports structured output formats (e.g., JSON Schema)
61
+ *
62
+ * @returns true if structured output is supported, false otherwise
63
+ */
64
+ supportsStructuredOutput() {
65
+ return this.supportsStructuredResponses;
66
+ }
67
+ }
68
+ class w extends y {
69
+ providerType = "local";
70
+ }
71
+ class $ extends y {
72
+ providerType = "remote";
73
+ }
74
+ const F = [
75
+ "null",
76
+ "",
77
+ "n/a",
78
+ "none",
79
+ "no value",
80
+ "empty",
81
+ "undefined",
82
+ "unknown",
83
+ "missing"
84
+ ], x = ["true", "yes", "1", "checked", "on"];
85
+ function h(o) {
86
+ o.dispatchEvent(new Event("input", { bubbles: !0 })), o.dispatchEvent(new Event("change", { bubbles: !0 }));
87
+ }
88
+ function S(o) {
89
+ return F.includes(o);
90
+ }
91
+ function M(o) {
92
+ if (o.id) {
93
+ const t = document.querySelector(`label[for="${o.id}"]`);
94
+ if (t)
95
+ return t.textContent?.trim() || "";
96
+ }
97
+ const e = o.closest("label");
98
+ return e && e.textContent?.trim() || "";
99
+ }
100
+ function T(o, e) {
101
+ let t = null;
102
+ const r = o.trim();
103
+ if (/^\d{4}-\d{2}-\d{2}/.test(r))
104
+ t = new Date(r);
105
+ else if (/^\d{1,2}[\/.-]\d{1,2}[\/.-]\d{2,4}$/.test(r)) {
106
+ const s = r.split(/[\/.-]/), c = parseInt(s[0], 10), u = parseInt(s[1], 10);
107
+ let f = parseInt(s[2], 10);
108
+ f < 100 && (f += 2e3), t = new Date(f, c - 1, u);
109
+ } else {
110
+ const s = Date.parse(r);
111
+ isNaN(s) || (t = new Date(s));
112
+ }
113
+ if (e === "time") {
114
+ const s = r.match(/(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\s*(am|pm))?/i);
115
+ if (s) {
116
+ let c = parseInt(s[1], 10);
117
+ const u = s[2], f = s[4]?.toLowerCase();
118
+ return f === "pm" && c < 12 && (c += 12), f === "am" && c === 12 && (c = 0), `${c.toString().padStart(2, "0")}:${u}`;
119
+ }
120
+ return null;
121
+ }
122
+ if (!t || isNaN(t.getTime()))
123
+ return null;
124
+ const i = t.getFullYear(), n = (t.getMonth() + 1).toString().padStart(2, "0"), a = t.getDate().toString().padStart(2, "0"), d = t.getHours().toString().padStart(2, "0"), p = t.getMinutes().toString().padStart(2, "0");
125
+ switch (e) {
126
+ case "date":
127
+ return `${i}-${n}-${a}`;
128
+ case "datetime-local":
129
+ return `${i}-${n}-${a}T${d}:${p}`;
130
+ case "month":
131
+ return `${i}-${n}`;
132
+ case "week":
133
+ const s = new Date(i, 0, 1), c = Math.floor((t.getTime() - s.getTime()) / (1440 * 60 * 1e3)), u = Math.ceil((c + s.getDay() + 1) / 7);
134
+ return `${i}-W${u.toString().padStart(2, "0")}`;
135
+ default:
136
+ return `${i}-${n}-${a}`;
137
+ }
138
+ }
139
+ function m(o) {
140
+ const e = {
141
+ element: o,
142
+ type: "text"
143
+ };
144
+ if (o instanceof HTMLInputElement ? (e.type = o.type, e.name = o.name, e.placeholder = o.placeholder, e.pattern = o.pattern, o.type === "checkbox" && (e.placeholder = o.value || "checkbox option"), o.type === "radio" && (e.placeholder = o.value || "radio option")) : o instanceof HTMLTextAreaElement ? (e.type = "textarea", e.name = o.name, e.placeholder = o.placeholder) : o instanceof HTMLSelectElement && (e.type = "select", e.name = o.name), o.id) {
145
+ const r = document.querySelector(`label[for="${o.id}"]`);
146
+ r && (e.label = r.textContent?.trim());
147
+ }
148
+ if (!e.label) {
149
+ const r = o.closest("label");
150
+ r && (e.label = r.textContent?.trim());
151
+ }
152
+ const t = o.dataset.affHint;
153
+ return t && (e.hint = t), e;
154
+ }
155
+ function k(o) {
156
+ const e = [], t = /* @__PURE__ */ new Map();
157
+ o.querySelectorAll(
158
+ 'input:not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="hidden"]):not([type="image"]):not([type="file"]), textarea, select'
159
+ ).forEach((i) => {
160
+ if (i instanceof HTMLInputElement && i.type === "radio") {
161
+ const n = i.name;
162
+ n && (t.has(n) || t.set(n, []), t.get(n).push(i));
163
+ } else i instanceof HTMLElement && e.push(m(i));
164
+ });
165
+ for (const [i, n] of t.entries()) {
166
+ if (n.length === 0) continue;
167
+ const a = n[0], d = m(a);
168
+ d.options = n.map((p) => {
169
+ let s = "";
170
+ if (p.id) {
171
+ const c = document.querySelector(`label[for="${p.id}"]`);
172
+ c && (s = c.textContent?.trim() || "");
173
+ }
174
+ if (!s) {
175
+ const c = p.closest("label");
176
+ c && (s = c.textContent?.trim() || "");
177
+ }
178
+ return {
179
+ value: p.value,
180
+ label: s || p.value
181
+ };
182
+ });
183
+ for (const p of n) {
184
+ const s = p.dataset.affHint;
185
+ s && (d.hint += " " + s);
186
+ }
187
+ e.push(d);
188
+ }
189
+ return e;
190
+ }
191
+ function A(o, e) {
192
+ const t = x.includes(e);
193
+ o.checked = t, h(o);
194
+ }
195
+ function L(o, e) {
196
+ const t = o.closest("form");
197
+ if (!t || !o.name) return;
198
+ const r = t.querySelectorAll(
199
+ `input[type="radio"][name="${o.name}"]`
200
+ );
201
+ for (const i of r) {
202
+ const n = M(i).toLowerCase(), a = i.value.toLowerCase();
203
+ if (a === e || n === e || a.includes(e) || n.includes(e) || e.includes(a) || e.includes(n)) {
204
+ i.checked = !0, h(i);
205
+ break;
206
+ }
207
+ }
208
+ }
209
+ function N(o, e) {
210
+ const t = T(e, o.type);
211
+ t ? (o.value = t, h(o)) : l.formFillDebug && console.warn(`Could not parse date value "${e}" for ${o.type} input`);
212
+ }
213
+ function D(o, e, t) {
214
+ let r = Array.from(o.options).find(
215
+ (i) => i.value.toLowerCase() === e || i.text.toLowerCase() === e
216
+ );
217
+ r || (r = Array.from(o.options).find(
218
+ (i) => i.value.toLowerCase().includes(e) || i.text.toLowerCase().includes(e) || e.includes(i.value.toLowerCase()) || e.includes(i.text.toLowerCase())
219
+ )), r ? (o.value = r.value, h(o)) : l.formFillDebug && console.warn(
220
+ `No matching option for select. Value: "${t}", Options:`,
221
+ Array.from(o.options).map((i) => `${i.value} (${i.text})`)
222
+ );
223
+ }
224
+ function g(o, e) {
225
+ const t = e.trim().toLowerCase();
226
+ if (!S(t))
227
+ if (o instanceof HTMLInputElement)
228
+ switch (o.type) {
229
+ case "checkbox":
230
+ A(o, t);
231
+ break;
232
+ case "radio":
233
+ L(o, t);
234
+ break;
235
+ case "date":
236
+ case "datetime-local":
237
+ case "time":
238
+ N(o, e);
239
+ break;
240
+ default:
241
+ o.value = e, h(o);
242
+ }
243
+ else o instanceof HTMLTextAreaElement ? (o.value = e, h(o)) : o instanceof HTMLSelectElement && D(o, t, e);
244
+ }
245
+ function P(o) {
246
+ return o.name || o.label || o.placeholder || "unknown";
247
+ }
248
+ function C(o, e) {
249
+ let t = `Generate appropriate content for the following form field:
250
+
251
+ `;
252
+ return o.label && (t += `Field Label: ${o.label}
253
+ `), o.name && (t += `Field Name: ${o.name}
254
+ `), t += `Field Type: ${o.type}
255
+ `, o.placeholder && (t += `Placeholder: ${o.placeholder}
256
+ `), o.pattern && (t += `Pattern/Format: ${o.pattern}
257
+ `), e && (t += `
258
+ Additional Context: ${e}
259
+ `), o.type === "checkbox" ? t = `${e}
260
+ Randomly return "true" or "false", no explanations. Dont repeat your choice too often.` : t += `
261
+ Provide a realistic and appropriate value for this field. Only return the value itself, no explanations.`, t;
262
+ }
263
+ function I(o, e) {
264
+ let t = `Extract structured data from the following unstructured text and match it to the form fields.
265
+
266
+ `;
267
+ t += `Form fields:
268
+ `;
269
+ for (const r of o) {
270
+ const i = r.name || r.label || r.placeholder || "unknown";
271
+ if (t += `- ${i} (type: ${r.type})`, r.label && (t += ` - Label: "${r.label}"`), r.placeholder && (t += ` - Placeholder: "${r.placeholder}"`), r.type === "select" && r.element instanceof HTMLSelectElement) {
272
+ const n = Array.from(r.element.options).map((a) => a.textContent?.trim() || "").filter((a) => a);
273
+ t += ` - Options: [${n.join(", ")}]`;
274
+ }
275
+ if (r.type === "radio" && r.options) {
276
+ const n = r.options.map((a) => a.label || a.value);
277
+ t += ` - Options: [${n.join(", ")}]`;
278
+ }
279
+ r.type === "date" ? t += " - Format: YYYY-MM-DD" : r.type === "datetime-local" ? t += " - Format: YYYY-MM-DDTHH:MM" : r.type === "time" && (t += " - Format: HH:MM"), r.hint && (t += ` - Additional info: ${r.hint}`), t += `
280
+ `;
281
+ }
282
+ return t += `
283
+ Unstructured text:
284
+ ${e}
285
+
286
+
287
+ Extract the relevant information and return it as a JSON object where keys match the field names exactly.
288
+
289
+
290
+ Only include fields where you found relevant data.
291
+
292
+
293
+ For checkbox fields, return "true" if the text indicates the option should be checked, "false" or omit otherwise.
294
+
295
+
296
+ For radio fields, return the value (preferred) or label of the selected option.
297
+
298
+
299
+ Return ONLY the JSON object, no explanations or markdown formatting.
300
+ `, t;
301
+ }
302
+ const v = {
303
+ FIELD_FILL: "You are a helpful assistant that generates appropriate content for form fields. Provide only the value to fill in the field, without any explanation or additional text.",
304
+ PARSE_EXTRACT: 'You are a helpful assistant that extracts structured data from unstructured text. You must respond ONLY with valid JSON, no explanations or markdown code blocks. If its a checkbox field, return "true" if it should be checked, otherwise return "false" or omit the field.'
305
+ };
306
+ function O(o) {
307
+ const e = {};
308
+ for (const t of o) {
309
+ const r = t.name || t.label;
310
+ if (!r) continue;
311
+ let i;
312
+ switch (t.type) {
313
+ case "number":
314
+ case "range":
315
+ i = { type: "number" };
316
+ break;
317
+ case "boolean":
318
+ case "checkbox":
319
+ i = { type: "boolean" };
320
+ break;
321
+ case "url":
322
+ i = { type: "string", format: "uri" };
323
+ break;
324
+ case "date":
325
+ i = { type: "string", format: "date" };
326
+ break;
327
+ case "datetime-local":
328
+ i = { type: "string", format: "date-time" };
329
+ break;
330
+ case "time":
331
+ i = { type: "string", format: "time" };
332
+ break;
333
+ default:
334
+ i = { type: "string" };
335
+ break;
336
+ }
337
+ if (t.pattern && (i.pattern = t.pattern), t.placeholder || t.hint) {
338
+ const n = [];
339
+ t.placeholder && n.push(t.placeholder), t.hint && n.push(t.hint), i.description = n.join(" - ");
340
+ }
341
+ e[r] = i;
342
+ }
343
+ return {
344
+ type: "object",
345
+ properties: e,
346
+ additionalProperties: !1
347
+ };
348
+ }
349
+ function R(o) {
350
+ try {
351
+ let e = o.trim();
352
+ e = e.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
353
+ const t = JSON.parse(e), r = {};
354
+ for (const [i, n] of Object.entries(t))
355
+ r[i] = String(n);
356
+ return r;
357
+ } catch (e) {
358
+ return console.error("Failed to parse JSON response:", e), console.error("Response was:", o), {};
359
+ }
360
+ }
361
+ function j(o) {
362
+ try {
363
+ return JSON.parse(o), !0;
364
+ } catch {
365
+ return !1;
366
+ }
367
+ }
368
+ class Y extends w {
369
+ providerName = "ollama";
370
+ supportsStructuredResponses = !0;
371
+ chatEndpoint;
372
+ listModelsEndpoint;
373
+ availabilityEndpoint;
374
+ constructor(e) {
375
+ super({
376
+ apiEndpoint: e?.apiEndpoint || l.ollama.apiEndpoint,
377
+ model: e?.model || l.ollama.model,
378
+ timeout: e?.timeout || l.timeout
379
+ }), this.chatEndpoint = this.apiEndpoint + "/api/chat", this.listModelsEndpoint = this.apiEndpoint + "/api/tags", this.availabilityEndpoint = this.apiEndpoint + "/api/tags";
380
+ }
381
+ async chat(e) {
382
+ const t = new AbortController(), r = setTimeout(() => t.abort(), this.timeout), i = this.chatEndpoint;
383
+ try {
384
+ const n = {
385
+ model: e.model,
386
+ messages: e.messages,
387
+ stream: !1,
388
+ options: {
389
+ num_predict: e.maxTokens
390
+ }
391
+ }, a = await fetch(i, {
392
+ method: "POST",
393
+ headers: {
394
+ "Content-Type": "application/json"
395
+ },
396
+ body: JSON.stringify(n),
397
+ signal: t.signal
398
+ });
399
+ if (!a.ok)
400
+ throw new Error(`Ollama API error: ${a.status} ${a.statusText}`);
401
+ const d = await a.json();
402
+ return {
403
+ content: d.message.content,
404
+ model: d.model,
405
+ finishReason: d.done ? "stop" : "length"
406
+ };
407
+ } catch (n) {
408
+ if (n instanceof Error) {
409
+ if (n.name === "AbortError")
410
+ throw new Error(`Ollama request timed out after ${this.timeout}ms`);
411
+ if (n.message.includes("fetch") || n.message.includes("Failed to fetch"))
412
+ throw new Error(`Failed to connect to Ollama at ${this.apiEndpoint}. Is Ollama running?`);
413
+ }
414
+ throw n;
415
+ } finally {
416
+ clearTimeout(r);
417
+ }
418
+ }
419
+ async listModels() {
420
+ try {
421
+ const e = await fetch(this.listModelsEndpoint);
422
+ if (!e.ok)
423
+ throw new Error(`Failed to fetch models: ${e.statusText}`);
424
+ return ((await e.json()).models || []).map((r) => r.name);
425
+ } catch (e) {
426
+ return console.error("Error listing Ollama models:", e), [];
427
+ }
428
+ }
429
+ async isAvailable() {
430
+ try {
431
+ return (await fetch(this.availabilityEndpoint, {
432
+ method: "GET"
433
+ })).ok;
434
+ } catch {
435
+ return !1;
436
+ }
437
+ }
438
+ }
439
+ class E extends $ {
440
+ providerName = "openai";
441
+ supportsStructuredResponses = !0;
442
+ chatEndpoint;
443
+ listModelsEndpoint;
444
+ availabilityEndpoint;
445
+ constructor(e) {
446
+ super({
447
+ apiEndpoint: e?.apiEndpoint || l.openai.apiEndpoint,
448
+ model: e?.model || l.openai.model,
449
+ timeout: e?.timeout || l.timeout
450
+ }), this.chatEndpoint = `${this.apiEndpoint}/${this.providerName}/chat`, this.listModelsEndpoint = `${this.apiEndpoint}/${this.providerName}/models`, this.availabilityEndpoint = `${this.apiEndpoint}/${this.providerName}/available`;
451
+ }
452
+ async chat(e) {
453
+ const t = new AbortController(), r = setTimeout(() => t.abort(), this.timeout), i = this.chatEndpoint;
454
+ try {
455
+ const a = await (await fetch(i, {
456
+ method: "POST",
457
+ headers: {
458
+ "Content-Type": "application/json"
459
+ },
460
+ body: JSON.stringify(e),
461
+ signal: t.signal
462
+ })).json();
463
+ return l.providerDebug && console.log(`${this.providerName} response body:`, a), {
464
+ content: a.choices[0].message.content,
465
+ model: a.model,
466
+ finishReason: a.choices[0].finish_reason
467
+ };
468
+ } catch (n) {
469
+ if (n instanceof Error) {
470
+ if (n.name === "AbortError")
471
+ throw new Error(`${this.providerName} request timed out after ${this.timeout}ms`);
472
+ if (n.message.includes("fetch") || n.message.includes("Failed to fetch"))
473
+ throw new Error(`Failed to connect to ${this.providerName}. Check your network connection.`);
474
+ }
475
+ throw n;
476
+ } finally {
477
+ clearTimeout(r);
478
+ }
479
+ }
480
+ async listModels() {
481
+ const e = this.listModelsEndpoint;
482
+ try {
483
+ const t = await fetch(e, { method: "POST" });
484
+ if (!t.ok)
485
+ throw new Error(`${this.providerName} API error: ${t.status} ${t.statusText}`);
486
+ return (await t.json()).models;
487
+ } catch (t) {
488
+ if (l.providerDebug)
489
+ throw new Error(`Error fetching models from ${this.providerName}: ${t}`);
490
+ return [];
491
+ }
492
+ }
493
+ async isAvailable() {
494
+ const e = this.availabilityEndpoint;
495
+ try {
496
+ return (await fetch(e, { method: "POST" })).ok;
497
+ } catch (t) {
498
+ if (l.providerDebug)
499
+ throw t;
500
+ return !1;
501
+ }
502
+ }
503
+ }
504
+ class H extends E {
505
+ providerName = "perplexity";
506
+ constructor(e) {
507
+ super({
508
+ apiEndpoint: e?.apiEndpoint || l.perplexity.apiEndpoint,
509
+ model: e?.model || l.perplexity.model,
510
+ timeout: e?.timeout || l.timeout
511
+ }), this.chatEndpoint = `${this.apiEndpoint}/${this.providerName}/chat`, this.listModelsEndpoint = `${this.apiEndpoint}/${this.providerName}/models`, this.availabilityEndpoint = `${this.apiEndpoint}/${this.providerName}/available`;
512
+ }
513
+ }
514
+ class b {
515
+ provider;
516
+ allowedProviders;
517
+ selectedFields;
518
+ constructor(e, t) {
519
+ e instanceof y ? this.provider = e : this.provider = b.constructProviderWithName(e, t), this.selectedFields = t?.targetFields, this.allowedProviders = t?.allowedProviders;
520
+ }
521
+ /**
522
+ * Fill a single form field with AI-generated content
523
+ *
524
+ * Generates appropriate content for one field based on its label, name,
525
+ * placeholder, and type. Useful for creative content or when you don't
526
+ * have source text to extract from.
527
+ *
528
+ * @param element - The form field element to fill (input, textarea, or select)
529
+ *
530
+ * @example
531
+ * ```typescript
532
+ * const bioField = document.querySelector('#bio');
533
+ * await aiForm.fillSingleField(bioField);
534
+ * ```
535
+ */
536
+ async fillSingleField(e) {
537
+ const t = m(e);
538
+ l.formFillDebug && console.log(`Filling ${t.type} field: ${t.name}`);
539
+ const r = C(t), i = [
540
+ {
541
+ role: "system",
542
+ content: v.FIELD_FILL
543
+ },
544
+ {
545
+ role: "user",
546
+ content: r
547
+ }
548
+ ];
549
+ try {
550
+ const n = await this.provider.chat({
551
+ messages: i,
552
+ model: this.provider.getSelectedModel()
553
+ });
554
+ n.content && g(e, n.content.trim()), l.formFillDebug && console.log("Field filled with:", n.content);
555
+ } catch (n) {
556
+ l.formFillDebug && console.error("Error during fillSingleField:", n);
557
+ }
558
+ }
559
+ /**
560
+ * Parse unstructured text and automatically fill matching form fields
561
+ *
562
+ * @param formElement - The HTML form to fill
563
+ * @param unstructuredText - The source text to extract data from
564
+ * - Examples: Resume text, email body, paragraph descriptions, JSON strings
565
+ */
566
+ async parseAndFillForm(e, t) {
567
+ const r = k(e);
568
+ l.formFillDebug && (console.log("Parsing unstructured text for", r.length, "fields"), console.log("Unstructured text:", r));
569
+ const i = this.selectedFields ? r.filter(
570
+ (s) => s.name && this.selectedFields.includes(s.name)
571
+ ) : r, n = I(i, t);
572
+ l.formFillDebug && (console.groupCollapsed("Constructed parse prompt:"), console.log(n), console.groupEnd(), console.log(`Sending prompt to ${this.provider.getName()}'s ${this.provider.getSelectedModel()} model...`));
573
+ const d = {
574
+ messages: [
575
+ {
576
+ role: "system",
577
+ content: v.PARSE_EXTRACT
578
+ },
579
+ {
580
+ role: "user",
581
+ content: n
582
+ }
583
+ ],
584
+ model: this.provider.getSelectedModel()
585
+ };
586
+ this.provider.supportsStructuredOutput() && (d.format = O(i), l.formFillDebug && console.log("Using structured output format:", d.format));
587
+ let p = {};
588
+ try {
589
+ const s = await this.provider.chat(d);
590
+ if (!s.content) {
591
+ l.formFillDebug && console.warn("No content received from AI provider.");
592
+ return;
593
+ }
594
+ p = R(s.content);
595
+ } catch (s) {
596
+ l.formFillDebug && console.error("Error calling AI provider:", s);
597
+ return;
598
+ }
599
+ l.formFillDebug && console.log("Extracted data:", p);
600
+ for (const s of i) {
601
+ const c = P(s);
602
+ if (c && p[c])
603
+ try {
604
+ g(s.element, p[c]);
605
+ } catch (u) {
606
+ l.formFillDebug && console.error(`Failed to fill field "${c}":`, u);
607
+ }
608
+ }
609
+ }
610
+ /**
611
+ * Get list of available models from the form's provider
612
+ */
613
+ async getAvailableModels() {
614
+ return this.provider.listModels ? await this.provider.listModels() : [];
615
+ }
616
+ /**
617
+ * Set the model to use for chat requests
618
+ */
619
+ async setSelectedModel(e) {
620
+ return this.provider.setSelectedModel(e);
621
+ }
622
+ /**
623
+ * Get the currently selected model
624
+ */
625
+ getSelectedModel() {
626
+ return this.provider.getSelectedModel();
627
+ }
628
+ /**
629
+ * Set which fields should be filled
630
+ */
631
+ setFields(e) {
632
+ this.selectedFields = e || void 0;
633
+ }
634
+ /**
635
+ * Get the currently configured field targets
636
+ *
637
+ * @returns Array of field names being targeted, or undefined if all fields are targeted
638
+ */
639
+ getFields() {
640
+ return this.selectedFields;
641
+ }
642
+ /**
643
+ * Check if the AI provider is available and responding
644
+ *
645
+ * @returns Promise resolving to true if provider is available, false otherwise
646
+ */
647
+ async providerAvailable() {
648
+ return this.provider.isAvailable ? await this.provider.isAvailable() : !0;
649
+ }
650
+ /**
651
+ * Change the AI provider
652
+ */
653
+ setProvider(e) {
654
+ this.provider = e;
655
+ }
656
+ /**
657
+ * Get the current AI provider
658
+ */
659
+ getProvider() {
660
+ return this.provider;
661
+ }
662
+ /**
663
+ * Get the list of allowed providers, if any
664
+ */
665
+ getListOfAllowedProviders() {
666
+ return this.allowedProviders;
667
+ }
668
+ /**
669
+ * Setup the AI provider based on the desired provider name
670
+ */
671
+ static constructProviderWithName(e, t) {
672
+ const r = {
673
+ apiEndpoint: t?.apiEndpoint || "",
674
+ model: t?.model || "",
675
+ timeout: t?.timeout
676
+ };
677
+ return {
678
+ ollama: () => new Y(r),
679
+ openai: () => new E(r),
680
+ perplexity: () => new H(r)
681
+ /**
682
+ * @extension Add more providers here as needed
683
+ */
684
+ }[e]();
685
+ }
686
+ }
687
+ function q(o = "aff-form") {
688
+ const e = document.getElementById(o), t = document.getElementById("aff-text"), r = document.getElementById("aff-text-button"), i = e.getAttribute("data-aff-provider") || "ollama", n = new b(i, { debug: !0 });
689
+ r ? r.addEventListener("click", async () => {
690
+ const a = t.value.trim();
691
+ try {
692
+ await n.parseAndFillForm(e, a);
693
+ } catch (d) {
694
+ console.error("Error filling form:", d);
695
+ }
696
+ }) : console.warn("AI Form Fill button not found");
697
+ }
698
+ export {
699
+ b as AIFormFill,
700
+ y as AIProvider,
701
+ Y as LocalOllamaProvider,
702
+ E as OpenAIProvider,
703
+ H as PerplexityProvider,
704
+ v as SYSTEM_PROMPTS,
705
+ l as affConfig,
706
+ m as analyzeField,
707
+ C as buildFieldPrompt,
708
+ I as buildParsePrompt,
709
+ P as getFieldIdentifier,
710
+ k as getFillTargets,
711
+ q as initializeAFFQuick,
712
+ j as isValidJson,
713
+ R as parseJsonResponse,
714
+ g as setFieldValue
715
+ };