accessify-widget 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "accessify-widget",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Accessify-Widget accessibility widget for any website",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/maddesv1-ctrl/accessify-widget",
@@ -36,6 +36,14 @@
36
36
  "dist/",
37
37
  "README.md"
38
38
  ],
39
+ "scripts": {
40
+ "dev": "vite build --watch",
41
+ "build": "vite build && BUILD_WIDGET=true vite build && node scripts/build-loader.js",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "test:e2e": "playwright test",
45
+ "lint": "eslint src/ --ext .ts,.svelte"
46
+ },
39
47
  "devDependencies": {
40
48
  "@axe-core/playwright": "^4.9.0",
41
49
  "@playwright/test": "^1.45.0",
@@ -49,13 +57,5 @@
49
57
  },
50
58
  "dependencies": {
51
59
  "axe-core": "^4.9.0"
52
- },
53
- "scripts": {
54
- "dev": "vite build --watch",
55
- "build": "vite build && BUILD_WIDGET=true vite build && node scripts/build-loader.js",
56
- "test": "vitest run",
57
- "test:watch": "vitest",
58
- "test:e2e": "playwright test",
59
- "lint": "eslint src/ --ext .ts,.svelte"
60
60
  }
61
- }
61
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Accessify Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,567 +0,0 @@
1
- import { R as RateLimitError } from "./index-qmiN2JAz.js";
2
- function createAltTextModule(aiService, lang = "de") {
3
- let enabled = false;
4
- let styleEl = null;
5
- let badgeEl = null;
6
- let activePanelEl = null;
7
- let missingAltImages = [];
8
- const processedImages = /* @__PURE__ */ new Map();
9
- const STYLE_ID = "accessify-alt-text-styles";
10
- const HIGHLIGHT_CLASS = "accessify-alt-missing";
11
- const BADGE_CLASS = "accessify-alt-badge";
12
- const PANEL_CLASS = "accessify-alt-panel";
13
- function getStyles() {
14
- return `
15
- .${HIGHLIGHT_CLASS} {
16
- outline: 3px dashed #e63946 !important;
17
- outline-offset: 3px !important;
18
- position: relative !important;
19
- cursor: pointer !important;
20
- transition: outline-color 0.2s ease !important;
21
- }
22
- .${HIGHLIGHT_CLASS}:hover {
23
- outline-color: #f77f00 !important;
24
- outline-style: solid !important;
25
- }
26
- .${HIGHLIGHT_CLASS}::after {
27
- content: 'ALT';
28
- position: absolute;
29
- top: 4px;
30
- left: 4px;
31
- padding: 2px 6px;
32
- background: #e63946;
33
- color: #fff;
34
- font-family: system-ui, -apple-system, sans-serif;
35
- font-size: 10px;
36
- font-weight: 700;
37
- border-radius: 3px;
38
- line-height: 1.4;
39
- pointer-events: none;
40
- z-index: 1;
41
- }
42
-
43
- .${HIGHLIGHT_CLASS}.accessify-alt-processed {
44
- outline-color: #2a9d8f !important;
45
- outline-style: solid !important;
46
- }
47
- .${HIGHLIGHT_CLASS}.accessify-alt-processed::after {
48
- content: '\\2713 ALT';
49
- background: #2a9d8f;
50
- }
51
-
52
- .${BADGE_CLASS} {
53
- position: fixed;
54
- top: 12px;
55
- right: 12px;
56
- z-index: 2147483646;
57
- display: flex;
58
- align-items: center;
59
- gap: 6px;
60
- padding: 8px 14px;
61
- background: #1a1a2e;
62
- color: #f0f0f0;
63
- border-radius: 8px;
64
- box-shadow: 0 4px 16px rgba(0,0,0,0.3);
65
- font-family: system-ui, -apple-system, sans-serif;
66
- font-size: 13px;
67
- user-select: none;
68
- animation: accessify-alt-fade-in 0.2s ease;
69
- }
70
- .${BADGE_CLASS}-count {
71
- display: inline-flex;
72
- align-items: center;
73
- justify-content: center;
74
- min-width: 22px;
75
- height: 22px;
76
- padding: 0 6px;
77
- background: #e63946;
78
- color: #fff;
79
- font-size: 12px;
80
- font-weight: 700;
81
- border-radius: 11px;
82
- }
83
-
84
- .${PANEL_CLASS} {
85
- position: absolute;
86
- z-index: 2147483646;
87
- width: 360px;
88
- max-width: 90vw;
89
- padding: 16px;
90
- background: #1a1a2e;
91
- color: #f0f0f0;
92
- border-radius: 12px;
93
- box-shadow: 0 8px 32px rgba(0,0,0,0.4);
94
- font-family: system-ui, -apple-system, sans-serif;
95
- font-size: 14px;
96
- line-height: 1.5;
97
- animation: accessify-alt-fade-in 0.2s ease;
98
- }
99
- .${PANEL_CLASS}-header {
100
- display: flex;
101
- justify-content: space-between;
102
- align-items: center;
103
- margin-bottom: 10px;
104
- }
105
- .${PANEL_CLASS}-header h3 {
106
- margin: 0;
107
- font-size: 14px;
108
- font-weight: 600;
109
- color: #4ea8de;
110
- }
111
- .${PANEL_CLASS}-close {
112
- display: inline-flex;
113
- align-items: center;
114
- justify-content: center;
115
- width: 24px;
116
- height: 24px;
117
- padding: 0;
118
- border: none;
119
- border-radius: 4px;
120
- background: transparent;
121
- color: #888;
122
- font-size: 16px;
123
- cursor: pointer;
124
- }
125
- .${PANEL_CLASS}-close:hover { color: #fff; background: rgba(255,255,255,0.1); }
126
- .${PANEL_CLASS}-close:focus-visible { outline: 2px solid #4ea8de; outline-offset: 1px; }
127
-
128
- .${PANEL_CLASS}-disclaimer {
129
- padding: 6px 10px;
130
- margin-bottom: 10px;
131
- background: rgba(255,193,7,0.1);
132
- border: 1px solid rgba(255,193,7,0.25);
133
- border-radius: 6px;
134
- font-size: 11px;
135
- color: #ffd166;
136
- }
137
-
138
- .${PANEL_CLASS}-preview {
139
- max-width: 100%;
140
- max-height: 120px;
141
- border-radius: 6px;
142
- margin-bottom: 10px;
143
- object-fit: contain;
144
- background: rgba(255,255,255,0.05);
145
- }
146
-
147
- .${PANEL_CLASS}-textarea {
148
- width: 100%;
149
- min-height: 60px;
150
- padding: 10px;
151
- border: 1px solid rgba(255,255,255,0.15);
152
- border-radius: 8px;
153
- background: rgba(255,255,255,0.04);
154
- color: #f0f0f0;
155
- font-family: inherit;
156
- font-size: 13px;
157
- line-height: 1.5;
158
- resize: vertical;
159
- margin-bottom: 10px;
160
- box-sizing: border-box;
161
- }
162
- .${PANEL_CLASS}-textarea:focus {
163
- border-color: #4ea8de;
164
- outline: none;
165
- }
166
- .${PANEL_CLASS}-textarea:read-only {
167
- opacity: 0.7;
168
- cursor: default;
169
- }
170
-
171
- .${PANEL_CLASS}-actions {
172
- display: flex;
173
- gap: 6px;
174
- justify-content: flex-end;
175
- }
176
- .${PANEL_CLASS}-actions button {
177
- padding: 6px 14px;
178
- border: 1px solid rgba(255,255,255,0.15);
179
- border-radius: 6px;
180
- background: rgba(255,255,255,0.08);
181
- color: #f0f0f0;
182
- font-size: 12px;
183
- cursor: pointer;
184
- transition: background 0.15s ease;
185
- }
186
- .${PANEL_CLASS}-actions button:hover { background: rgba(255,255,255,0.18); }
187
- .${PANEL_CLASS}-actions button:focus-visible { outline: 2px solid #4ea8de; outline-offset: 1px; }
188
- .${PANEL_CLASS}-actions button.primary {
189
- background: rgba(78,168,222,0.2);
190
- color: #4ea8de;
191
- border-color: rgba(78,168,222,0.3);
192
- }
193
- .${PANEL_CLASS}-actions button.primary:hover { background: rgba(78,168,222,0.35); }
194
- .${PANEL_CLASS}-actions button.success {
195
- background: rgba(42,157,143,0.2);
196
- color: #2a9d8f;
197
- border-color: rgba(42,157,143,0.3);
198
- }
199
- .${PANEL_CLASS}-actions button.success:hover { background: rgba(42,157,143,0.35); }
200
-
201
- .${PANEL_CLASS}-spinner {
202
- display: flex;
203
- align-items: center;
204
- gap: 10px;
205
- padding: 16px 0;
206
- justify-content: center;
207
- color: #888;
208
- font-size: 13px;
209
- }
210
- .${PANEL_CLASS}-spinner::before {
211
- content: '';
212
- width: 18px;
213
- height: 18px;
214
- border: 2px solid rgba(78,168,222,0.3);
215
- border-top-color: #4ea8de;
216
- border-radius: 50%;
217
- animation: accessify-alt-spin 0.6s linear infinite;
218
- }
219
-
220
- .${PANEL_CLASS}-error {
221
- padding: 10px;
222
- background: rgba(220,53,69,0.12);
223
- border: 1px solid rgba(220,53,69,0.3);
224
- border-radius: 8px;
225
- color: #f0a0a8;
226
- font-size: 13px;
227
- margin-bottom: 10px;
228
- }
229
-
230
- @keyframes accessify-alt-fade-in {
231
- from { opacity: 0; transform: translateY(4px); }
232
- to { opacity: 1; transform: translateY(0); }
233
- }
234
- @keyframes accessify-alt-spin {
235
- to { transform: rotate(360deg); }
236
- }
237
- `;
238
- }
239
- function injectStyles() {
240
- if (document.getElementById(STYLE_ID)) return;
241
- styleEl = document.createElement("style");
242
- styleEl.id = STYLE_ID;
243
- styleEl.textContent = getStyles();
244
- document.head.appendChild(styleEl);
245
- }
246
- function removeStyles() {
247
- styleEl?.remove();
248
- styleEl = null;
249
- document.getElementById(STYLE_ID)?.remove();
250
- }
251
- function scanForMissingAlt() {
252
- const allImages = document.querySelectorAll("img");
253
- const missing = [];
254
- allImages.forEach((img) => {
255
- if (img.closest("#accessify-root")) return;
256
- if (img.width < 20 || img.height < 20) return;
257
- const alt = img.getAttribute("alt");
258
- if (alt === null || alt.trim() === "") {
259
- missing.push(img);
260
- }
261
- });
262
- return missing;
263
- }
264
- function highlightImages() {
265
- missingAltImages = scanForMissingAlt();
266
- missingAltImages.forEach((img) => {
267
- img.classList.add(HIGHLIGHT_CLASS);
268
- if (processedImages.has(img) && processedImages.get(img).applied) {
269
- img.classList.add("accessify-alt-processed");
270
- }
271
- img.addEventListener("click", handleImageClick);
272
- });
273
- updateBadge();
274
- }
275
- function removeHighlights() {
276
- missingAltImages.forEach((img) => {
277
- img.classList.remove(HIGHLIGHT_CLASS, "accessify-alt-processed");
278
- img.removeEventListener("click", handleImageClick);
279
- });
280
- missingAltImages = [];
281
- }
282
- function updateBadge() {
283
- removeBadge();
284
- const remaining = missingAltImages.filter(
285
- (img) => !processedImages.has(img) || !processedImages.get(img).applied
286
- ).length;
287
- if (remaining === 0 && missingAltImages.length > 0) {
288
- showBadge(
289
- lang.startsWith("de") ? "Alle Bilder haben Alt-Text!" : "All images have alt text!",
290
- "#2a9d8f",
291
- 0
292
- );
293
- return;
294
- }
295
- if (remaining === 0) return;
296
- showBadge(
297
- lang.startsWith("de") ? `${remaining} ${remaining === 1 ? "Bild" : "Bilder"} ohne Alt-Text` : `${remaining} ${remaining === 1 ? "image" : "images"} missing alt text`,
298
- "#e63946",
299
- remaining
300
- );
301
- }
302
- function showBadge(text, color, count) {
303
- const badge = document.createElement("div");
304
- badge.className = BADGE_CLASS;
305
- badge.setAttribute("role", "status");
306
- badge.setAttribute("aria-live", "polite");
307
- if (count > 0) {
308
- const countEl = document.createElement("span");
309
- countEl.className = `${BADGE_CLASS}-count`;
310
- countEl.style.background = color;
311
- countEl.textContent = String(count);
312
- badge.appendChild(countEl);
313
- }
314
- const label = document.createElement("span");
315
- label.textContent = text;
316
- badge.appendChild(label);
317
- document.body.appendChild(badge);
318
- badgeEl = badge;
319
- }
320
- function removeBadge() {
321
- badgeEl?.remove();
322
- badgeEl = null;
323
- }
324
- function handleImageClick(e) {
325
- e.preventDefault();
326
- e.stopPropagation();
327
- const img = e.currentTarget;
328
- showAltTextPanel(img);
329
- }
330
- function showAltTextPanel(img) {
331
- removeActivePanel();
332
- const panel = document.createElement("div");
333
- panel.className = PANEL_CLASS;
334
- panel.setAttribute("role", "dialog");
335
- panel.setAttribute("aria-label", lang.startsWith("de") ? "Alt-Text erzeugen" : "Generate Alt Text");
336
- const imgRect = img.getBoundingClientRect();
337
- const top = imgRect.bottom + window.scrollY + 8;
338
- const left = Math.min(
339
- Math.max(8, imgRect.left + window.scrollX),
340
- window.innerWidth - 380
341
- );
342
- panel.style.top = `${top}px`;
343
- panel.style.left = `${left}px`;
344
- const header = document.createElement("div");
345
- header.className = `${PANEL_CLASS}-header`;
346
- const title = document.createElement("h3");
347
- title.textContent = lang.startsWith("de") ? "Alt-Text erzeugen" : "Generate Alt Text";
348
- const closeBtn = document.createElement("button");
349
- closeBtn.className = `${PANEL_CLASS}-close`;
350
- closeBtn.setAttribute("aria-label", lang.startsWith("de") ? "Schließen" : "Close");
351
- closeBtn.innerHTML = "&times;";
352
- closeBtn.addEventListener("click", () => removeActivePanel());
353
- header.appendChild(title);
354
- header.appendChild(closeBtn);
355
- panel.appendChild(header);
356
- const disclaimer = document.createElement("div");
357
- disclaimer.className = `${PANEL_CLASS}-disclaimer`;
358
- disclaimer.textContent = lang.startsWith("de") ? "KI-generiert – bitte überprüfen" : "AI-generated – please review";
359
- panel.appendChild(disclaimer);
360
- const preview = document.createElement("img");
361
- preview.className = `${PANEL_CLASS}-preview`;
362
- preview.src = img.src;
363
- preview.alt = "Preview of image being described";
364
- panel.appendChild(preview);
365
- const existing = processedImages.get(img);
366
- if (existing && existing.generatedAlt) {
367
- renderEditableResult(panel, img, existing.generatedAlt, existing.applied);
368
- } else {
369
- generateAndShow(panel, img);
370
- }
371
- document.body.appendChild(panel);
372
- activePanelEl = panel;
373
- closeBtn.focus();
374
- const handleEsc = (e) => {
375
- if (e.key === "Escape") {
376
- removeActivePanel();
377
- document.removeEventListener("keydown", handleEsc);
378
- }
379
- };
380
- document.addEventListener("keydown", handleEsc);
381
- }
382
- async function generateAndShow(panel, img) {
383
- if (!aiService) {
384
- showPanelError(
385
- panel,
386
- lang.startsWith("de") ? "Bitte konfigurieren Sie einen OpenRouter API-Schlüssel, um KI-generierte Bildbeschreibungen zu nutzen." : "Please configure an OpenRouter API key to use AI alt-text generation."
387
- );
388
- return;
389
- }
390
- const spinner = document.createElement("div");
391
- spinner.className = `${PANEL_CLASS}-spinner`;
392
- spinner.textContent = lang.startsWith("de") ? "Alt-Text wird erzeugt..." : "Generating alt text...";
393
- panel.appendChild(spinner);
394
- try {
395
- const context = gatherImageContext(img);
396
- const altText = await aiService.generateAltText(img.src, context);
397
- spinner.remove();
398
- if (!altText) {
399
- showPanelError(
400
- panel,
401
- lang.startsWith("de") ? "Keine Beschreibung erhalten. Bitte versuchen Sie es erneut." : "No description received. Please try again."
402
- );
403
- return;
404
- }
405
- processedImages.set(img, {
406
- element: img,
407
- generatedAlt: altText,
408
- applied: false
409
- });
410
- renderEditableResult(panel, img, altText, false);
411
- } catch (err) {
412
- spinner.remove();
413
- if (err instanceof RateLimitError) {
414
- showPanelError(
415
- panel,
416
- lang.startsWith("de") ? "Kostenlose API-Grenze erreicht. Bitte versuchen Sie es in einer Minute erneut oder hinterlegen Sie einen eigenen API-Schlüssel." : "Free tier rate limit reached. Please try again in a minute or configure your own API key."
417
- );
418
- } else {
419
- const msg = err?.message || "";
420
- const is401 = msg.includes("401");
421
- showPanelError(
422
- panel,
423
- is401 ? lang.startsWith("de") ? "OpenRouter API-Schlüssel fehlt oder ungültig. Erstellen Sie einen kostenlosen Key auf openrouter.ai und übergeben Sie ihn als openRouterKey in der Widget-Konfiguration." : "OpenRouter API key missing or invalid. Create a free key at openrouter.ai and pass it as openRouterKey in the widget config." : lang.startsWith("de") ? "Fehler beim Erzeugen des Alt-Texts. Bitte versuchen Sie es erneut." : "Error generating alt text. Please try again."
424
- );
425
- }
426
- }
427
- }
428
- function renderEditableResult(panel, img, altText, alreadyApplied) {
429
- panel.querySelectorAll(
430
- `.${PANEL_CLASS}-textarea, .${PANEL_CLASS}-actions, .${PANEL_CLASS}-error, .${PANEL_CLASS}-spinner`
431
- ).forEach((el) => el.remove());
432
- const textarea = document.createElement("textarea");
433
- textarea.className = `${PANEL_CLASS}-textarea`;
434
- textarea.value = altText;
435
- textarea.readOnly = true;
436
- textarea.setAttribute("aria-label", lang.startsWith("de") ? "Alt-Text Beschreibung" : "Alt text description");
437
- panel.appendChild(textarea);
438
- const actions = document.createElement("div");
439
- actions.className = `${PANEL_CLASS}-actions`;
440
- const editBtn = document.createElement("button");
441
- editBtn.textContent = lang.startsWith("de") ? "Bearbeiten" : "Edit";
442
- editBtn.addEventListener("click", () => {
443
- textarea.readOnly = !textarea.readOnly;
444
- if (!textarea.readOnly) {
445
- textarea.focus();
446
- editBtn.textContent = lang.startsWith("de") ? "Fertig" : "Done";
447
- } else {
448
- editBtn.textContent = lang.startsWith("de") ? "Bearbeiten" : "Edit";
449
- const entry = processedImages.get(img);
450
- if (entry) entry.generatedAlt = textarea.value;
451
- }
452
- });
453
- const regenBtn = document.createElement("button");
454
- regenBtn.textContent = lang.startsWith("de") ? "Neu erzeugen" : "Regenerate";
455
- regenBtn.addEventListener("click", () => {
456
- processedImages.delete(img);
457
- textarea.remove();
458
- actions.remove();
459
- generateAndShow(panel, img);
460
- });
461
- const applyBtn = document.createElement("button");
462
- applyBtn.classList.add(alreadyApplied ? "success" : "primary");
463
- applyBtn.textContent = alreadyApplied ? lang.startsWith("de") ? "Angewendet" : "Applied" : lang.startsWith("de") ? "Übernehmen" : "Apply";
464
- applyBtn.addEventListener("click", () => {
465
- const finalText = textarea.value.trim();
466
- if (!finalText) return;
467
- img.alt = finalText;
468
- img.setAttribute("alt", finalText);
469
- const entry = processedImages.get(img) || {
470
- element: img,
471
- generatedAlt: finalText,
472
- applied: false
473
- };
474
- entry.applied = true;
475
- entry.generatedAlt = finalText;
476
- processedImages.set(img, entry);
477
- img.classList.add("accessify-alt-processed");
478
- applyBtn.classList.remove("primary");
479
- applyBtn.classList.add("success");
480
- applyBtn.textContent = lang.startsWith("de") ? "Angewendet!" : "Applied!";
481
- updateBadge();
482
- setTimeout(() => removeActivePanel(), 1200);
483
- });
484
- actions.appendChild(editBtn);
485
- actions.appendChild(regenBtn);
486
- actions.appendChild(applyBtn);
487
- panel.appendChild(actions);
488
- }
489
- function showPanelError(panel, message) {
490
- const errorEl = document.createElement("div");
491
- errorEl.className = `${PANEL_CLASS}-error`;
492
- errorEl.setAttribute("role", "alert");
493
- errorEl.textContent = message;
494
- panel.appendChild(errorEl);
495
- }
496
- function removeActivePanel() {
497
- activePanelEl?.remove();
498
- activePanelEl = null;
499
- }
500
- function gatherImageContext(img) {
501
- const parts = [];
502
- if (img.title) parts.push(`Title: ${img.title}`);
503
- try {
504
- const url = new URL(img.src, window.location.href);
505
- const filename = url.pathname.split("/").pop();
506
- if (filename) parts.push(`Filename: ${filename}`);
507
- } catch {
508
- }
509
- const figure = img.closest("figure");
510
- if (figure) {
511
- const caption = figure.querySelector("figcaption");
512
- if (caption?.textContent?.trim()) {
513
- parts.push(`Caption: ${caption.textContent.trim()}`);
514
- }
515
- }
516
- const parent = img.parentElement;
517
- if (parent) {
518
- const heading = parent.querySelector("h1, h2, h3, h4, h5, h6");
519
- if (heading?.textContent?.trim()) {
520
- parts.push(`Nearby heading: ${heading.textContent.trim()}`);
521
- }
522
- }
523
- const describedBy = img.getAttribute("aria-describedby");
524
- if (describedBy) {
525
- const descEl = document.getElementById(describedBy);
526
- if (descEl?.textContent?.trim()) {
527
- parts.push(`Description: ${descEl.textContent.trim()}`);
528
- }
529
- }
530
- return parts.join(". ");
531
- }
532
- function activate() {
533
- if (enabled) return;
534
- enabled = true;
535
- injectStyles();
536
- highlightImages();
537
- }
538
- function deactivate() {
539
- enabled = false;
540
- removeActivePanel();
541
- removeHighlights();
542
- removeBadge();
543
- removeStyles();
544
- }
545
- return {
546
- id: "alt-text",
547
- name: () => lang.startsWith("de") ? "Alt-Text erzeugen" : "Alt-Text Generator",
548
- description: lang.startsWith("de") ? "Bildbeschreibungen automatisch erstellen" : "Auto-generate image descriptions",
549
- icon: "alt-text",
550
- category: "ai",
551
- activate,
552
- deactivate,
553
- getState: () => ({
554
- id: "alt-text",
555
- enabled,
556
- value: {
557
- missingCount: missingAltImages.length,
558
- processedCount: processedImages.size,
559
- appliedCount: [...processedImages.values()].filter((p) => p.applied).length
560
- }
561
- })
562
- };
563
- }
564
- export {
565
- createAltTextModule as default
566
- };
567
- //# sourceMappingURL=alt-text-CgzNGvdT.js.map