accessify-widget 0.2.0 → 0.2.2

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.
@@ -254,8 +254,6 @@ https://svelte.dev/e/svelte_boundary_reset_onerror`);
254
254
  }
255
255
  const EACH_ITEM_REACTIVE = 1;
256
256
  const EACH_INDEX_REACTIVE = 1 << 1;
257
- const EACH_IS_CONTROLLED = 1 << 2;
258
- const EACH_IS_ANIMATED = 1 << 3;
259
257
  const EACH_ITEM_IMMUTABLE = 1 << 4;
260
258
  const PROPS_IS_IMMUTABLE = 1;
261
259
  const PROPS_IS_UPDATED = 1 << 2;
@@ -279,15 +277,6 @@ https://svelte.dev/e/lifecycle_double_unmount`, bold, normal);
279
277
  console.warn(`https://svelte.dev/e/lifecycle_double_unmount`);
280
278
  }
281
279
  }
282
- function select_multiple_invalid_value() {
283
- if (DEV) {
284
- console.warn(`%c[svelte] select_multiple_invalid_value
285
- %cThe \`value\` property of a \`<select multiple>\` element should be an array, but it received a non-array value. The selection will be kept as is.
286
- https://svelte.dev/e/select_multiple_invalid_value`, bold, normal);
287
- } else {
288
- console.warn(`https://svelte.dev/e/select_multiple_invalid_value`);
289
- }
290
- }
291
280
  function state_proxy_equality_mismatch(operator) {
292
281
  if (DEV) {
293
282
  console.warn(`%c[svelte] state_proxy_equality_mismatch
@@ -2319,9 +2308,6 @@ function get_proxied_value(value) {
2319
2308
  }
2320
2309
  return value;
2321
2310
  }
2322
- function is(a, b) {
2323
- return Object.is(get_proxied_value(a), get_proxied_value(b));
2324
- }
2325
2311
  const ARRAY_MUTATING_METHODS = /* @__PURE__ */ new Set([
2326
2312
  "copyWithin",
2327
2313
  "fill",
@@ -2476,7 +2462,7 @@ function clear_text_content(node) {
2476
2462
  function should_defer_append() {
2477
2463
  return false;
2478
2464
  }
2479
- function create_element(tag2, namespace, is2) {
2465
+ function create_element(tag2, namespace, is) {
2480
2466
  let options = void 0;
2481
2467
  return (
2482
2468
  /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */
@@ -3421,13 +3407,39 @@ function from_html(content, flags2) {
3421
3407
  return clone;
3422
3408
  };
3423
3409
  }
3424
- function comment() {
3425
- var frag = document.createDocumentFragment();
3426
- var start = document.createComment("");
3427
- var anchor = create_text();
3428
- frag.append(start, anchor);
3429
- assign_nodes(start, anchor);
3430
- return frag;
3410
+ // @__NO_SIDE_EFFECTS__
3411
+ function from_namespace(content, flags2, ns = "svg") {
3412
+ var has_start = !content.startsWith("<!>");
3413
+ var wrapped = `<${ns}>${has_start ? content : "<!>" + content}</${ns}>`;
3414
+ var node;
3415
+ return () => {
3416
+ if (!node) {
3417
+ var fragment = (
3418
+ /** @type {DocumentFragment} */
3419
+ create_fragment_from_html(wrapped)
3420
+ );
3421
+ var root2 = (
3422
+ /** @type {Element} */
3423
+ /* @__PURE__ */ get_first_child(fragment)
3424
+ );
3425
+ {
3426
+ node = /** @type {Element} */
3427
+ /* @__PURE__ */ get_first_child(root2);
3428
+ }
3429
+ }
3430
+ var clone = (
3431
+ /** @type {TemplateNode} */
3432
+ node.cloneNode(true)
3433
+ );
3434
+ {
3435
+ assign_nodes(clone, clone);
3436
+ }
3437
+ return clone;
3438
+ };
3439
+ }
3440
+ // @__NO_SIDE_EFFECTS__
3441
+ function from_svg(content, flags2) {
3442
+ return /* @__PURE__ */ from_namespace(content, flags2, "svg");
3431
3443
  }
3432
3444
  function append(anchor, dom) {
3433
3445
  if (anchor === null) {
@@ -3822,8 +3834,7 @@ var offscreen_anchor;
3822
3834
  function each(node, flags2, get_collection, get_key, render_fn2, fallback_fn = null) {
3823
3835
  var anchor = node;
3824
3836
  var items = /* @__PURE__ */ new Map();
3825
- var is_controlled = (flags2 & EACH_IS_CONTROLLED) !== 0;
3826
- if (is_controlled) {
3837
+ {
3827
3838
  var parent_node = (
3828
3839
  /** @type {Element} */
3829
3840
  node
@@ -3948,31 +3959,17 @@ function skip_to_branch(effect2) {
3948
3959
  return effect2;
3949
3960
  }
3950
3961
  function reconcile(state2, array, anchor, flags2, get_key) {
3951
- var is_animated = (flags2 & EACH_IS_ANIMATED) !== 0;
3952
3962
  var length = array.length;
3953
3963
  var items = state2.items;
3954
3964
  var current = skip_to_branch(state2.effect.first);
3955
3965
  var seen;
3956
3966
  var prev = null;
3957
- var to_animate;
3958
3967
  var matched = [];
3959
3968
  var stashed = [];
3960
3969
  var value;
3961
3970
  var key;
3962
3971
  var effect2;
3963
3972
  var i;
3964
- if (is_animated) {
3965
- for (i = 0; i < length; i += 1) {
3966
- value = array[i];
3967
- key = get_key(value, i);
3968
- effect2 = /** @type {EachItem} */
3969
- items.get(key).e;
3970
- if ((effect2.f & EFFECT_OFFSCREEN) === 0) {
3971
- effect2.nodes?.a?.measure();
3972
- (to_animate ?? (to_animate = /* @__PURE__ */ new Set())).add(effect2);
3973
- }
3974
- }
3975
- }
3976
3973
  for (i = 0; i < length; i += 1) {
3977
3974
  value = array[i];
3978
3975
  key = get_key(value, i);
@@ -4007,10 +4004,6 @@ function reconcile(state2, array, anchor, flags2, get_key) {
4007
4004
  }
4008
4005
  if ((effect2.f & INERT) !== 0) {
4009
4006
  resume_effect(effect2);
4010
- if (is_animated) {
4011
- effect2.nodes?.a?.unfix();
4012
- (to_animate ?? (to_animate = /* @__PURE__ */ new Set())).delete(effect2);
4013
- }
4014
4007
  }
4015
4008
  if (effect2 !== current) {
4016
4009
  if (seen !== void 0 && seen.has(effect2)) {
@@ -4089,26 +4082,10 @@ function reconcile(state2, array, anchor, flags2, get_key) {
4089
4082
  }
4090
4083
  var destroy_length = to_destroy.length;
4091
4084
  if (destroy_length > 0) {
4092
- var controlled_anchor = (flags2 & EACH_IS_CONTROLLED) !== 0 && length === 0 ? anchor : null;
4093
- if (is_animated) {
4094
- for (i = 0; i < destroy_length; i += 1) {
4095
- to_destroy[i].nodes?.a?.measure();
4096
- }
4097
- for (i = 0; i < destroy_length; i += 1) {
4098
- to_destroy[i].nodes?.a?.fix();
4099
- }
4100
- }
4085
+ var controlled_anchor = length === 0 ? anchor : null;
4101
4086
  pause_effects(state2, to_destroy, controlled_anchor);
4102
4087
  }
4103
4088
  }
4104
- if (is_animated) {
4105
- queue_micro_task(() => {
4106
- if (to_animate === void 0) return;
4107
- for (effect2 of to_animate) {
4108
- effect2.nodes?.a?.apply();
4109
- }
4110
- });
4111
- }
4112
4089
  }
4113
4090
  function create_item(items, anchor, value, key, index2, render_fn2, flags2, get_collection) {
4114
4091
  var v = (flags2 & EACH_ITEM_REACTIVE) !== 0 ? (flags2 & EACH_ITEM_IMMUTABLE) === 0 ? /* @__PURE__ */ mutable_source(value, false, false) : source(value) : null;
@@ -4245,55 +4222,52 @@ function html(node, get_value, is_controlled = false, svg = false, mathml = fals
4245
4222
  }
4246
4223
  });
4247
4224
  }
4248
- function select_option(select, value, mounting = false) {
4249
- if (select.multiple) {
4250
- if (value == void 0) {
4251
- return;
4252
- }
4253
- if (!is_array(value)) {
4254
- return select_multiple_invalid_value();
4255
- }
4256
- for (var option of select.options) {
4257
- option.selected = value.includes(get_option_value(option));
4258
- }
4259
- return;
4260
- }
4261
- for (option of select.options) {
4262
- var option_value = get_option_value(option);
4263
- if (is(option_value, value)) {
4264
- option.selected = true;
4265
- return;
4225
+ const whitespace = [..." \n\r\f \v\uFEFF"];
4226
+ function to_class(value, hash, directives) {
4227
+ var classname = value == null ? "" : "" + value;
4228
+ if (directives) {
4229
+ for (var key of Object.keys(directives)) {
4230
+ if (directives[key]) {
4231
+ classname = classname ? classname + " " + key : key;
4232
+ } else if (classname.length) {
4233
+ var len = key.length;
4234
+ var a = 0;
4235
+ while ((a = classname.indexOf(key, a)) >= 0) {
4236
+ var b = a + len;
4237
+ if ((a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b]))) {
4238
+ classname = (a === 0 ? "" : classname.substring(0, a)) + classname.substring(b + 1);
4239
+ } else {
4240
+ a = b;
4241
+ }
4242
+ }
4243
+ }
4266
4244
  }
4267
4245
  }
4268
- if (!mounting || value !== void 0) {
4269
- select.selectedIndex = -1;
4270
- }
4271
- }
4272
- function init_select(select) {
4273
- var observer = new MutationObserver(() => {
4274
- select_option(select, select.__value);
4275
- });
4276
- observer.observe(select, {
4277
- // Listen to option element changes
4278
- childList: true,
4279
- subtree: true,
4280
- // because of <optgroup>
4281
- // Listen to option element value attribute changes
4282
- // (doesn't get notified of select value changes,
4283
- // because that property is not reflected as an attribute)
4284
- attributes: true,
4285
- attributeFilter: ["value"]
4286
- });
4287
- teardown(() => {
4288
- observer.disconnect();
4289
- });
4246
+ return classname === "" ? null : classname;
4290
4247
  }
4291
- function get_option_value(option) {
4292
- if ("__value" in option) {
4293
- return option.__value;
4294
- } else {
4295
- return option.value;
4248
+ function set_class(dom, is_html, value, hash, prev_classes, next_classes) {
4249
+ var prev = dom.__className;
4250
+ if (prev !== value || prev === void 0) {
4251
+ var next_class_name = to_class(value, hash, next_classes);
4252
+ {
4253
+ if (next_class_name == null) {
4254
+ dom.removeAttribute("class");
4255
+ } else if (is_html) {
4256
+ dom.className = next_class_name;
4257
+ } else {
4258
+ dom.setAttribute("class", next_class_name);
4259
+ }
4260
+ }
4261
+ dom.__className = value;
4262
+ } else if (next_classes && prev_classes !== next_classes) {
4263
+ for (var key in next_classes) {
4264
+ var is_present = !!next_classes[key];
4265
+ if (prev_classes == null || is_present !== !!prev_classes[key]) {
4266
+ dom.classList.toggle(key, is_present);
4267
+ }
4268
+ }
4296
4269
  }
4270
+ return next_classes;
4297
4271
  }
4298
4272
  const IS_CUSTOM_ELEMENT = Symbol("is custom element");
4299
4273
  const IS_HTML = Symbol("is html");
@@ -4781,7 +4755,7 @@ const deJson = {
4781
4755
  "feature.linkHighlight": "Links hervorheben",
4782
4756
  "feature.linkHighlight.desc": "Alle Links sichtbar unterstreichen",
4783
4757
  "feature.keyboardNav": "Tastaturnavigation",
4784
- "feature.keyboardNav.desc": "Erweiterte Tastatursteuerung und Shortcuts",
4758
+ "feature.keyboardNav.desc": "Seite nur mit Tastatur bedienen: Direkt zum Inhalt springen, mit Alt+H durch Überschriften navigieren, Alt+L für Seitenbereiche, Alt+K zeigt alle Tastenkombinationen",
4785
4759
  "feature.tts": "Vorlesen",
4786
4760
  "feature.tts.desc": "Text per Klick vorlesen lassen",
4787
4761
  "feature.textSimplify": "Text vereinfachen",
@@ -4870,7 +4844,7 @@ const enJson = {
4870
4844
  "feature.linkHighlight": "Highlight Links",
4871
4845
  "feature.linkHighlight.desc": "Underline and highlight all links",
4872
4846
  "feature.keyboardNav": "Keyboard Nav",
4873
- "feature.keyboardNav.desc": "Enhanced keyboard controls and shortcuts",
4847
+ "feature.keyboardNav.desc": "Navigate the page using only your keyboard: Skip to main content, Alt+H to jump through headings, Alt+L for page sections, Alt+K shows all shortcuts",
4874
4848
  "feature.tts": "Read Aloud",
4875
4849
  "feature.tts.desc": "Click any text to hear it spoken",
4876
4850
  "feature.textSimplify": "Simplify Text",
@@ -4915,6 +4889,8 @@ const EN_BASE = {
4915
4889
  ...enJson,
4916
4890
  "widget.subtitle": "Adjust contrast, text and navigation on this page",
4917
4891
  "widget.language": "Language",
4892
+ "widget.widgetLang": "Widget language",
4893
+ "widget.moveWidget": "Move widget to other side",
4918
4894
  "widget.poweredBy": "Powered by Accessify-Widget",
4919
4895
  "section.essentials": "Most used",
4920
4896
  "section.essentials.desc": "Start with contrast, text size and keyboard support.",
@@ -4946,6 +4922,8 @@ const DE_BASE = {
4946
4922
  ...deJson,
4947
4923
  "widget.subtitle": "Passe Kontrast, Text und Navigation auf dieser Seite an",
4948
4924
  "widget.language": "Sprache",
4925
+ "widget.widgetLang": "Widget-Sprache",
4926
+ "widget.moveWidget": "Widget auf andere Seite verschieben",
4949
4927
  "widget.poweredBy": "Bereitgestellt von Accessify-Widget",
4950
4928
  "section.essentials": "Am haeufigsten genutzt",
4951
4929
  "section.essentials.desc": "Starte mit Kontrast, Textgroesse und Tastaturhilfe.",
@@ -4980,6 +4958,7 @@ const locales = {
4980
4958
  en: EN_BASE,
4981
4959
  de: DE_BASE,
4982
4960
  zh: withEnglish({
4961
+ "widget.widgetLang": "小组件语言",
4983
4962
  "widget.title": "无障碍",
4984
4963
  "widget.subtitle": "调整此页面的对比度、文字和导航",
4985
4964
  "widget.open": "打开无障碍设置",
@@ -5022,6 +5001,7 @@ const locales = {
5022
5001
  "feature.autoScan": "WCAG 扫描"
5023
5002
  }),
5024
5003
  hi: withEnglish({
5004
+ "widget.widgetLang": "विजेट भाषा",
5025
5005
  "widget.title": "सुलभता",
5026
5006
  "widget.subtitle": "इस पेज पर कंट्रास्ट, टेक्स्ट और नेविगेशन समायोजित करें",
5027
5007
  "widget.open": "सुलभता सेटिंग खोलें",
@@ -5064,6 +5044,7 @@ const locales = {
5064
5044
  "feature.autoScan": "WCAG स्कैन"
5065
5045
  }),
5066
5046
  es: withEnglish({
5047
+ "widget.widgetLang": "Idioma del widget",
5067
5048
  "widget.title": "Accesibilidad",
5068
5049
  "widget.subtitle": "Ajusta contraste, texto y navegacion en esta pagina",
5069
5050
  "widget.open": "Abrir ajustes de accesibilidad",
@@ -5106,6 +5087,7 @@ const locales = {
5106
5087
  "feature.autoScan": "Escaneo WCAG"
5107
5088
  }),
5108
5089
  fr: withEnglish({
5090
+ "widget.widgetLang": "Langue du widget",
5109
5091
  "widget.title": "Accessibilite",
5110
5092
  "widget.subtitle": "Ajustez le contraste, le texte et la navigation sur cette page",
5111
5093
  "widget.open": "Ouvrir les reglages d'accessibilite",
@@ -5148,6 +5130,7 @@ const locales = {
5148
5130
  "feature.autoScan": "Analyse WCAG"
5149
5131
  }),
5150
5132
  ar: withEnglish({
5133
+ "widget.widgetLang": "لغة الأداة",
5151
5134
  "widget.title": "إمكانية الوصول",
5152
5135
  "widget.subtitle": "اضبط التباين والنص والتنقل في هذه الصفحة",
5153
5136
  "widget.open": "افتح إعدادات إمكانية الوصول",
@@ -5232,6 +5215,7 @@ const locales = {
5232
5215
  "feature.autoScan": "WCAG স্ক্যান"
5233
5216
  }),
5234
5217
  pt: withEnglish({
5218
+ "widget.widgetLang": "Idioma do widget",
5235
5219
  "widget.title": "Acessibilidade",
5236
5220
  "widget.subtitle": "Ajuste contraste, texto e navegacao nesta pagina",
5237
5221
  "widget.open": "Abrir configuracoes de acessibilidade",
@@ -5274,6 +5258,7 @@ const locales = {
5274
5258
  "feature.autoScan": "Varredura WCAG"
5275
5259
  }),
5276
5260
  ru: withEnglish({
5261
+ "widget.widgetLang": "Язык виджета",
5277
5262
  "widget.title": "Доступность",
5278
5263
  "widget.subtitle": "Настройте контраст, текст и навигацию на этой странице",
5279
5264
  "widget.open": "Открыть настройки доступности",
@@ -5400,6 +5385,7 @@ const locales = {
5400
5385
  "feature.autoScan": "Pindai WCAG"
5401
5386
  }),
5402
5387
  ja: withEnglish({
5388
+ "widget.widgetLang": "ウィジェットの言語",
5403
5389
  "widget.title": "アクセシビリティ",
5404
5390
  "widget.subtitle": "このページのコントラスト、文字、ナビゲーションを調整します",
5405
5391
  "widget.open": "アクセシビリティ設定を開く",
@@ -5568,6 +5554,7 @@ const locales = {
5568
5554
  "feature.autoScan": "WCAG స్కాన్"
5569
5555
  }),
5570
5556
  tr: withEnglish({
5557
+ "widget.widgetLang": "Widget dili",
5571
5558
  "widget.title": "Erisilebilirlik",
5572
5559
  "widget.subtitle": "Bu sayfada kontrast, metin ve gezinmeyi ayarlayin",
5573
5560
  "widget.open": "Erisilebilirlik ayarlarini ac",
@@ -5694,6 +5681,7 @@ const locales = {
5694
5681
  "feature.autoScan": "Quet WCAG"
5695
5682
  }),
5696
5683
  ko: withEnglish({
5684
+ "widget.widgetLang": "위젯 언어",
5697
5685
  "widget.title": "접근성",
5698
5686
  "widget.subtitle": "이 페이지의 대비, 텍스트, 내비게이션을 조정하세요",
5699
5687
  "widget.open": "접근성 설정 열기",
@@ -5737,26 +5725,16 @@ const locales = {
5737
5725
  })
5738
5726
  };
5739
5727
  const LANGUAGE_OPTIONS = [
5740
- { code: "en", label: "English" },
5741
- { code: "zh", label: "中文" },
5742
- { code: "hi", label: "हिन्दी" },
5743
- { code: "es", label: "Español" },
5744
- { code: "fr", label: "Français" },
5745
- { code: "ar", label: "العربية" },
5746
- { code: "bn", label: "বাংলা" },
5747
- { code: "pt", label: "Português" },
5748
- { code: "ru", label: "Русский" },
5749
- { code: "ur", label: "اردو" },
5750
- { code: "id", label: "Bahasa Indonesia" },
5751
- { code: "de", label: "Deutsch" },
5752
- { code: "ja", label: "日本語" },
5753
- { code: "pcm", label: "Naijá" },
5754
- { code: "mr", label: "मराठी" },
5755
- { code: "te", label: "తెలుగు" },
5756
- { code: "tr", label: "Türkçe" },
5757
- { code: "ta", label: "தமிழ்" },
5758
- { code: "vi", label: "Tiếng Việt" },
5759
- { code: "ko", label: "한국어" }
5728
+ { code: "de", label: "Deutsch", flag: "🇩🇪" },
5729
+ { code: "en", label: "English", flag: "🇺🇸" },
5730
+ { code: "es", label: "Español", flag: "🇪🇸" },
5731
+ { code: "fr", label: "Français", flag: "🇫🇷" },
5732
+ { code: "pt", label: "Português", flag: "🇧🇷" },
5733
+ { code: "tr", label: "Türkçe", flag: "🇹🇷" },
5734
+ { code: "ar", label: "العربية", flag: "🇸🇦" },
5735
+ { code: "zh", label: "中文", flag: "🇨🇳" },
5736
+ { code: "ja", label: "日本語", flag: "🇯🇵" },
5737
+ { code: "ru", label: "Русский", flag: "🇷🇺" }
5760
5738
  ];
5761
5739
  const RTL_LANGS = /* @__PURE__ */ new Set(["ar", "ur"]);
5762
5740
  let currentWidgetLang = "en";
@@ -5780,15 +5758,75 @@ function t(key, lang = "en") {
5780
5758
  const resolved = getSupportedLang(lang);
5781
5759
  return locales[resolved]?.[key] || EN_BASE[key] || key;
5782
5760
  }
5783
- var root$5 = /* @__PURE__ */ from_html(`<button class="accessify-trigger"></button>`);
5761
+ var root$5 = /* @__PURE__ */ from_html(`<button></button>`);
5784
5762
  function TriggerButton($$anchor, $$props) {
5785
5763
  push($$props, true);
5786
5764
  let lang = prop($$props, "lang", 3, "en"), el = prop($$props, "el", 15, null);
5765
+ let isDragging = /* @__PURE__ */ state(false);
5766
+ onMount(() => {
5767
+ const btn = el();
5768
+ if (!btn) return;
5769
+ let dragStartX = 0;
5770
+ let dragStartY = 0;
5771
+ let startLeft = 0;
5772
+ let startTop = 0;
5773
+ let hasMoved = false;
5774
+ let pointerId = null;
5775
+ function onDown(e) {
5776
+ if ($$props.isOpen) return;
5777
+ const rect = btn.getBoundingClientRect();
5778
+ dragStartX = e.clientX;
5779
+ dragStartY = e.clientY;
5780
+ startLeft = rect.left;
5781
+ startTop = rect.top;
5782
+ hasMoved = false;
5783
+ pointerId = e.pointerId;
5784
+ btn.setPointerCapture(e.pointerId);
5785
+ }
5786
+ function onMove(e) {
5787
+ if (pointerId === null || e.pointerId !== pointerId) return;
5788
+ const dx = e.clientX - dragStartX;
5789
+ const dy = e.clientY - dragStartY;
5790
+ if (!hasMoved && (Math.abs(dx) > 5 || Math.abs(dy) > 5)) {
5791
+ hasMoved = true;
5792
+ set(isDragging, true);
5793
+ btn.style.transition = "none";
5794
+ }
5795
+ if (hasMoved) {
5796
+ const newLeft = Math.max(0, Math.min(window.innerWidth - 60, startLeft + dx));
5797
+ const newTop = Math.max(0, Math.min(window.innerHeight - 60, startTop + dy));
5798
+ btn.style.left = newLeft + "px";
5799
+ btn.style.top = newTop + "px";
5800
+ btn.style.right = "auto";
5801
+ btn.style.bottom = "auto";
5802
+ }
5803
+ }
5804
+ function onUp(e) {
5805
+ if (pointerId === null || e.pointerId !== pointerId) return;
5806
+ btn.releasePointerCapture(e.pointerId);
5807
+ btn.style.transition = "";
5808
+ set(isDragging, false);
5809
+ if (!hasMoved) {
5810
+ $$props.onclick();
5811
+ }
5812
+ pointerId = null;
5813
+ }
5814
+ btn.addEventListener("pointerdown", onDown);
5815
+ btn.addEventListener("pointermove", onMove);
5816
+ btn.addEventListener("pointerup", onUp);
5817
+ return () => {
5818
+ btn.removeEventListener("pointerdown", onDown);
5819
+ btn.removeEventListener("pointermove", onMove);
5820
+ btn.removeEventListener("pointerup", onUp);
5821
+ };
5822
+ });
5787
5823
  var button = root$5();
5824
+ let classes;
5788
5825
  html(button, () => $$props.isOpen ? iconClose() : iconAccessibility(), true);
5789
5826
  bind_this(button, ($$value) => el($$value), () => el());
5790
5827
  template_effect(
5791
5828
  ($0) => {
5829
+ classes = set_class(button, 1, "accessify-trigger", null, classes, { "accessify-trigger--dragging": get(isDragging) });
5792
5830
  set_attribute(button, "aria-expanded", $$props.isOpen);
5793
5831
  set_attribute(button, "aria-label", $0);
5794
5832
  },
@@ -5796,98 +5834,26 @@ function TriggerButton($$anchor, $$props) {
5796
5834
  () => $$props.isOpen ? t("widget.close", lang()) : t("widget.open", lang())
5797
5835
  ]
5798
5836
  );
5799
- delegated("click", button, function(...$$args) {
5800
- $$props.onclick?.apply(this, $$args);
5801
- });
5802
5837
  append($$anchor, button);
5803
5838
  pop();
5804
5839
  }
5805
- delegate(["click"]);
5806
- var root_2$1 = /* @__PURE__ */ from_html(`<option> </option>`);
5807
- var root_1$3 = /* @__PURE__ */ from_html(`<label class="accessify-language-picker"><span class="sr-only"> </span> <span class="accessify-language-icon" aria-hidden="true"></span> <select class="accessify-language-select"></select></label>`);
5808
- var root$4 = /* @__PURE__ */ from_html(`<div class="accessify-header"><div class="accessify-header-copy"><span class="accessify-header-kicker"> </span> <div class="accessify-header-title"><h2> </h2> <p> </p></div></div> <div class="accessify-header-toolbar"><!> <div class="accessify-header-actions"><button class="accessify-header-btn"><!> <span> </span></button> <button class="accessify-header-btn accessify-header-btn--icon"></button></div></div></div>`);
5840
+ var root$4 = /* @__PURE__ */ from_html(`<header class="accessify-header"><div class="accessify-header-left"><span class="accessify-logo" aria-label="Accessify"><svg viewBox="0 0 480 80" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><text x="0" y="64" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="72" font-weight="700" letter-spacing="-2">accessify</text></svg></span></div> <div class="accessify-header-actions"><button class="accessify-header-btn"></button> <button class="accessify-header-btn accessify-header-btn--icon accessify-header-btn--close"></button></div></header>`);
5809
5841
  function PanelHeader($$anchor, $$props) {
5810
5842
  push($$props, true);
5811
5843
  let lang = prop($$props, "lang", 3, "en");
5812
- function handleLanguageChange(event2) {
5813
- const target = event2.currentTarget;
5814
- $$props.onlangchange?.(target.value);
5815
- }
5816
- var div = root$4();
5817
- var div_1 = child(div);
5818
- var span = child(div_1);
5819
- var text = child(span);
5820
- var div_2 = sibling(span, 2);
5821
- var h2 = child(div_2);
5822
- var text_1 = child(h2);
5823
- var p = sibling(h2, 2);
5824
- var text_2 = child(p);
5825
- var div_3 = sibling(div_1, 2);
5826
- var node = child(div_3);
5827
- {
5828
- var consequent = ($$anchor2) => {
5829
- var label = root_1$3();
5830
- var span_1 = child(label);
5831
- var text_3 = child(span_1);
5832
- var span_2 = sibling(span_1, 2);
5833
- html(span_2, iconGlobe, true);
5834
- var select = sibling(span_2, 2);
5835
- each(select, 21, () => LANGUAGE_OPTIONS, index, ($$anchor3, option) => {
5836
- var option_1 = root_2$1();
5837
- var text_4 = child(option_1);
5838
- var option_1_value = {};
5839
- template_effect(() => {
5840
- set_text(text_4, get(option).label);
5841
- if (option_1_value !== (option_1_value = get(option).code)) {
5842
- option_1.value = (option_1.__value = get(option).code) ?? "";
5843
- }
5844
- });
5845
- append($$anchor3, option_1);
5846
- });
5847
- var select_value;
5848
- init_select(select);
5849
- template_effect(
5850
- ($0, $1) => {
5851
- set_text(text_3, $0);
5852
- set_attribute(select, "aria-label", $1);
5853
- if (select_value !== (select_value = lang())) {
5854
- select.value = (select.__value = lang()) ?? "", select_option(select, lang());
5855
- }
5856
- },
5857
- [
5858
- () => t("widget.language", lang()),
5859
- () => t("widget.language", lang())
5860
- ]
5861
- );
5862
- delegated("change", select, handleLanguageChange);
5863
- append($$anchor2, label);
5864
- };
5865
- if_block(node, ($$render) => {
5866
- if ($$props.onlangchange) $$render(consequent);
5867
- });
5868
- }
5869
- var div_4 = sibling(node, 2);
5870
- var button = child(div_4);
5871
- var node_1 = child(button);
5872
- html(node_1, iconReset);
5873
- var span_3 = sibling(node_1, 2);
5874
- var text_5 = child(span_3);
5844
+ prop($$props, "activeCount", 3, 0);
5845
+ var header = root$4();
5846
+ var div = sibling(child(header), 2);
5847
+ var button = child(div);
5848
+ html(button, iconReset, true);
5875
5849
  var button_1 = sibling(button, 2);
5876
5850
  html(button_1, iconClose, true);
5877
5851
  template_effect(
5878
- ($0, $1, $2, $3, $4, $5) => {
5879
- set_text(text, $0);
5880
- set_text(text_1, $1);
5881
- set_text(text_2, $2);
5882
- set_attribute(button, "aria-label", $3);
5883
- set_text(text_5, $4);
5884
- set_attribute(button_1, "aria-label", $5);
5852
+ ($0, $1) => {
5853
+ set_attribute(button, "aria-label", $0);
5854
+ set_attribute(button_1, "aria-label", $1);
5885
5855
  },
5886
5856
  [
5887
- () => t("control.mostUsed", lang()),
5888
- () => t("widget.title", lang()),
5889
- () => t("widget.subtitle", lang()),
5890
- () => t("widget.reset", lang()),
5891
5857
  () => t("widget.reset", lang()),
5892
5858
  () => t("widget.close", lang())
5893
5859
  ]
@@ -5898,37 +5864,130 @@ function PanelHeader($$anchor, $$props) {
5898
5864
  delegated("click", button_1, function(...$$args) {
5899
5865
  $$props.onclose?.apply(this, $$args);
5900
5866
  });
5901
- append($$anchor, div);
5867
+ append($$anchor, header);
5902
5868
  pop();
5903
5869
  }
5904
- delegate(["change", "click"]);
5905
- var root_1$2 = /* @__PURE__ */ from_html(`<button class="accessify-status-reset"> </button>`);
5906
- var root$3 = /* @__PURE__ */ from_html(`<div class="accessify-status" role="status" aria-live="polite"><span class="accessify-status-count"> </span> <!></div>`);
5907
- function StatusBar($$anchor, $$props) {
5870
+ delegate(["click"]);
5871
+ const PROFILES = [
5872
+ {
5873
+ id: "vision-impaired",
5874
+ nameKey: "profile.visionImpaired",
5875
+ descKey: "profile.visionImpaired.desc",
5876
+ iconId: "eye",
5877
+ features: ["contrast", "text-size", "big-cursor", "link-highlight"],
5878
+ featureSettings: { "text-size": 150, contrast: "high" }
5879
+ },
5880
+ {
5881
+ id: "motor-impaired",
5882
+ nameKey: "profile.motorImpaired",
5883
+ descKey: "profile.motorImpaired.desc",
5884
+ iconId: "keyboard",
5885
+ features: ["keyboard-nav", "big-cursor", "link-highlight"]
5886
+ },
5887
+ {
5888
+ id: "cognitive",
5889
+ nameKey: "profile.cognitive",
5890
+ descKey: "profile.cognitive.desc",
5891
+ iconId: "brain",
5892
+ features: ["reading-guide", "text-size", "spacing", "dyslexia-font", "animation-stop"],
5893
+ featureSettings: { "text-size": 130 }
5894
+ },
5895
+ {
5896
+ id: "seizure-safe",
5897
+ nameKey: "profile.seizureSafe",
5898
+ descKey: "profile.seizureSafe.desc",
5899
+ iconId: "shield",
5900
+ features: ["animation-stop", "saturation"],
5901
+ featureSettings: { saturation: "low" }
5902
+ },
5903
+ {
5904
+ id: "adhd-friendly",
5905
+ nameKey: "profile.adhdFriendly",
5906
+ descKey: "profile.adhdFriendly.desc",
5907
+ iconId: "focus",
5908
+ features: ["animation-stop", "reading-mask", "hide-images"]
5909
+ }
5910
+ ];
5911
+ var root_3$2 = /* @__PURE__ */ from_svg(`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"></path></svg>`);
5912
+ var root_2$2 = /* @__PURE__ */ from_html(`<button class="accessify-accordion-option"><span class="accessify-accordion-option-left"><span class="accessify-accordion-option-icon" aria-hidden="true"></span> <span class="accessify-accordion-option-text"><span class="accessify-accordion-option-name"> </span> <span class="accessify-accordion-option-desc"> </span></span></span> <!></button>`);
5913
+ var root_1$3 = /* @__PURE__ */ from_html(`<div class="accessify-accordion-list" role="group"></div>`);
5914
+ var root$3 = /* @__PURE__ */ from_html(`<div class="accessify-accordion" role="region"><button class="accessify-accordion-toggle"><span class="accessify-accordion-toggle-left"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M12 8v4l2 2"></path></svg> <span> </span></span> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <!></div>`);
5915
+ function ProfileSection($$anchor, $$props) {
5908
5916
  push($$props, true);
5909
- let activeCount = prop($$props, "activeCount", 3, 0), lang = prop($$props, "lang", 3, "en");
5910
- let statusText = /* @__PURE__ */ user_derived(() => activeCount() > 0 ? t("status.active", lang()).replace("{count}", String(activeCount())) : t("status.noActive", lang()));
5917
+ let lang = prop($$props, "lang", 3, "en"), activeProfileId = prop($$props, "activeProfileId", 3, null);
5918
+ let isOpen = /* @__PURE__ */ state(false);
5919
+ function getIcon(iconId) {
5920
+ const iconFn = icons[iconId];
5921
+ return iconFn ? iconFn() : "";
5922
+ }
5923
+ let activeProfile = /* @__PURE__ */ user_derived(() => activeProfileId() ? PROFILES.find((p) => p.id === activeProfileId()) : null);
5911
5924
  var div = root$3();
5912
- var span = child(div);
5913
- var text = child(span);
5914
- var node = sibling(span, 2);
5925
+ var button = child(div);
5926
+ var span = child(button);
5927
+ var span_1 = sibling(child(span), 2);
5928
+ var text = child(span_1);
5929
+ var svg = sibling(span, 2);
5930
+ let classes;
5931
+ var node = sibling(button, 2);
5915
5932
  {
5916
- var consequent = ($$anchor2) => {
5917
- var button = root_1$2();
5918
- var text_1 = child(button);
5919
- template_effect(($0) => set_text(text_1, $0), [() => t("widget.reset", lang())]);
5920
- delegated("click", button, function(...$$args) {
5921
- $$props.onreset?.apply(this, $$args);
5933
+ var consequent_1 = ($$anchor2) => {
5934
+ var div_1 = root_1$3();
5935
+ each(div_1, 21, () => PROFILES, (profile) => profile.id, ($$anchor3, profile) => {
5936
+ var button_1 = root_2$2();
5937
+ var span_2 = child(button_1);
5938
+ var span_3 = child(span_2);
5939
+ html(span_3, () => getIcon(get(profile).iconId), true);
5940
+ var span_4 = sibling(span_3, 2);
5941
+ var span_5 = child(span_4);
5942
+ var text_1 = child(span_5);
5943
+ var span_6 = sibling(span_5, 2);
5944
+ var text_2 = child(span_6);
5945
+ var node_1 = sibling(span_2, 2);
5946
+ {
5947
+ var consequent = ($$anchor4) => {
5948
+ var svg_1 = root_3$2();
5949
+ append($$anchor4, svg_1);
5950
+ };
5951
+ if_block(node_1, ($$render) => {
5952
+ if (activeProfileId() === get(profile).id) $$render(consequent);
5953
+ });
5954
+ }
5955
+ template_effect(
5956
+ ($0, $1) => {
5957
+ set_attribute(button_1, "aria-pressed", activeProfileId() === get(profile).id);
5958
+ set_attribute(button_1, "data-active", activeProfileId() === get(profile).id);
5959
+ set_text(text_1, $0);
5960
+ set_text(text_2, $1);
5961
+ },
5962
+ [
5963
+ () => t(get(profile).nameKey, lang()),
5964
+ () => t(get(profile).descKey, lang())
5965
+ ]
5966
+ );
5967
+ delegated("click", button_1, () => $$props.onactivate(get(profile)));
5968
+ append($$anchor3, button_1);
5922
5969
  });
5923
- append($$anchor2, button);
5970
+ template_effect(($0) => set_attribute(div_1, "aria-label", $0), [() => t("section.profiles", lang())]);
5971
+ append($$anchor2, div_1);
5924
5972
  };
5925
5973
  if_block(node, ($$render) => {
5926
- if (activeCount() > 0) $$render(consequent);
5974
+ if (get(isOpen)) $$render(consequent_1);
5927
5975
  });
5928
5976
  }
5929
- template_effect(() => {
5930
- set_attribute(span, "data-count", activeCount());
5931
- set_text(text, get(statusText));
5977
+ template_effect(
5978
+ ($0, $1) => {
5979
+ set_attribute(div, "aria-label", $0);
5980
+ set_attribute(button, "aria-expanded", get(isOpen));
5981
+ set_text(text, $1);
5982
+ classes = set_class(svg, 0, "accessify-accordion-chevron", null, classes, { "accessify-accordion-chevron--open": get(isOpen) });
5983
+ },
5984
+ [
5985
+ () => t("section.profiles", lang()),
5986
+ () => get(activeProfile) ? t(get(activeProfile).nameKey, lang()) : t("section.profiles", lang())
5987
+ ]
5988
+ );
5989
+ delegated("click", button, () => {
5990
+ set(isOpen, !get(isOpen));
5932
5991
  });
5933
5992
  append($$anchor, div);
5934
5993
  pop();
@@ -5937,10 +5996,13 @@ delegate(["click"]);
5937
5996
  const DEFAULT_PROXY = "https://accessify-api.accessify.workers.dev";
5938
5997
  const OPENROUTER_BASE = "https://openrouter.ai/api/v1";
5939
5998
  const DEFAULTS = {
5940
- textModel: "qwen/qwen3-next-80b-a3b-instruct:free",
5941
- visionModel: "meta-llama/llama-3.3-70b-instruct:free"
5999
+ textModel: "google/gemini-2.0-flash-001",
6000
+ visionModel: "google/gemini-2.0-flash-001"
5942
6001
  };
5943
6002
  function createAIService(config2) {
6003
+ if (config2.apiKey) {
6004
+ return createDirectService(config2);
6005
+ }
5944
6006
  if (config2.siteKey) {
5945
6007
  return createProxyService(config2.siteKey, config2.proxyUrl);
5946
6008
  }
@@ -5954,6 +6016,42 @@ function createProxyService(siteKey, proxyUrl) {
5954
6016
  };
5955
6017
  return {
5956
6018
  async simplifyText(text, level = "einfache", lang = "de") {
6019
+ const blockId = `b-${simpleHash(text)}`;
6020
+ const variant = lang.startsWith("de") ? `de-${level}` : "en-plain";
6021
+ try {
6022
+ const cacheRes = await fetch(`${base}/v1/cache/query`, {
6023
+ method: "POST",
6024
+ headers,
6025
+ body: JSON.stringify({
6026
+ pageUrl: typeof window !== "undefined" ? window.location.href : "",
6027
+ feature: "simplify",
6028
+ variant,
6029
+ blocks: [{ id: blockId, text }]
6030
+ })
6031
+ });
6032
+ if (cacheRes.ok) {
6033
+ const cacheData = await cacheRes.json();
6034
+ if (cacheData.cached?.[blockId]) return cacheData.cached[blockId];
6035
+ }
6036
+ } catch {
6037
+ }
6038
+ try {
6039
+ const processRes = await fetch(`${base}/v1/cache/process`, {
6040
+ method: "POST",
6041
+ headers,
6042
+ body: JSON.stringify({
6043
+ pageUrl: typeof window !== "undefined" ? window.location.href : "",
6044
+ feature: "simplify",
6045
+ variant,
6046
+ blocks: [{ id: blockId, text }]
6047
+ })
6048
+ });
6049
+ if (processRes.ok) {
6050
+ const processData = await processRes.json();
6051
+ if (processData.results?.[blockId]) return processData.results[blockId];
6052
+ }
6053
+ } catch {
6054
+ }
5957
6055
  const res = await fetch(`${base}/v1/ai/simplify`, {
5958
6056
  method: "POST",
5959
6057
  headers,
@@ -5967,11 +6065,11 @@ function createProxyService(siteKey, proxyUrl) {
5967
6065
  const data = await res.json();
5968
6066
  return data.result || "";
5969
6067
  },
5970
- async generateAltText(imageUrl, context) {
6068
+ async generateAltText(imageUrl, context, lang) {
5971
6069
  const res = await fetch(`${base}/v1/ai/alt-text`, {
5972
6070
  method: "POST",
5973
6071
  headers,
5974
- body: JSON.stringify({ imageUrl, context })
6072
+ body: JSON.stringify({ imageUrl, context, lang })
5975
6073
  });
5976
6074
  if (!res.ok) {
5977
6075
  const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
@@ -5983,6 +6081,25 @@ function createProxyService(siteKey, proxyUrl) {
5983
6081
  }
5984
6082
  };
5985
6083
  }
6084
+ function simpleHash(str) {
6085
+ let hash = 5381;
6086
+ for (let i = 0; i < str.length; i++) {
6087
+ hash = (hash << 5) + hash + str.charCodeAt(i);
6088
+ hash |= 0;
6089
+ }
6090
+ return Math.abs(hash).toString(36);
6091
+ }
6092
+ function extractContent(data) {
6093
+ const msg = data.choices?.[0]?.message;
6094
+ if (msg?.content && typeof msg.content === "string") return msg.content.trim();
6095
+ if (msg?.reasoning && typeof msg.reasoning === "string") return msg.reasoning.trim();
6096
+ if (Array.isArray(msg?.reasoning_details)) {
6097
+ for (const detail of msg.reasoning_details) {
6098
+ if (detail?.text) return detail.text.trim();
6099
+ }
6100
+ }
6101
+ return "";
6102
+ }
5986
6103
  function createDirectService(config2) {
5987
6104
  const headers = {
5988
6105
  "Content-Type": "application/json",
@@ -6011,9 +6128,10 @@ function createDirectService(config2) {
6011
6128
  throw new Error(`AI service error: ${response.status}`);
6012
6129
  }
6013
6130
  const data = await response.json();
6014
- return data.choices?.[0]?.message?.content?.trim() || "";
6131
+ return extractContent(data);
6015
6132
  },
6016
- async generateAltText(imageUrl, context) {
6133
+ async generateAltText(imageUrl, context, lang) {
6134
+ const langInstruction = lang && !lang.startsWith("en") ? ` Generate the alt text in ${lang} language.` : "";
6017
6135
  const response = await fetch(`${OPENROUTER_BASE}/chat/completions`, {
6018
6136
  method: "POST",
6019
6137
  headers,
@@ -6024,12 +6142,12 @@ function createDirectService(config2) {
6024
6142
  content: [
6025
6143
  {
6026
6144
  type: "text",
6027
- text: `Generate a concise, descriptive alt text for this image for web accessibility. ${context ? "Context: " + context : ""} Return ONLY the alt text, nothing else. Max 125 characters.`
6145
+ text: `Generate a concise, descriptive alt text for this image for web accessibility.${langInstruction} ${context ? "Context: " + context : ""} Return ONLY the alt text, nothing else. Max 125 characters.`
6028
6146
  },
6029
6147
  { type: "image_url", image_url: { url: imageUrl } }
6030
6148
  ]
6031
6149
  }],
6032
- max_tokens: 100
6150
+ max_tokens: 300
6033
6151
  })
6034
6152
  });
6035
6153
  if (!response.ok) {
@@ -6037,7 +6155,7 @@ function createDirectService(config2) {
6037
6155
  throw new Error(`AI vision error: ${response.status}`);
6038
6156
  }
6039
6157
  const data = await response.json();
6040
- return data.choices?.[0]?.message?.content?.trim() || "";
6158
+ return extractContent(data);
6041
6159
  }
6042
6160
  };
6043
6161
  }
@@ -6082,16 +6200,12 @@ function getEnglishSimplificationPrompt() {
6082
6200
 
6083
6201
  Return ONLY the simplified text.`;
6084
6202
  }
6085
- var root_3 = /* @__PURE__ */ from_html(`<button class="accessify-chip"> </button>`);
6086
- var root_2 = /* @__PURE__ */ from_html(`<article class="accessify-control-card accessify-control-card--contrast"><div class="accessify-control-copy"><span class="accessify-card-icon" aria-hidden="true"></span> <div><h4> </h4> <p> </p></div></div> <div class="accessify-chip-group" role="group"></div></article>`);
6087
- var root_5 = /* @__PURE__ */ from_html(`<button class="accessify-chip"> </button>`);
6088
- var root_4 = /* @__PURE__ */ from_html(`<article class="accessify-control-card accessify-control-card--text-size"><div class="accessify-control-copy"><span class="accessify-card-icon" aria-hidden="true"></span> <div><h4> </h4> <p> </p></div></div> <div class="accessify-chip-group accessify-chip-group--sizes" role="group"></div></article>`);
6089
- var root_7 = /* @__PURE__ */ from_html(`<button class="accessify-card" role="switch"><div class="accessify-card-top"><span class="accessify-card-icon" aria-hidden="true"></span></div> <span class="accessify-card-label"> </span> <span class="accessify-card-desc"> </span></button>`);
6090
- var root_6 = /* @__PURE__ */ from_html(`<div class="accessify-features"></div>`);
6091
- var root_1$1 = /* @__PURE__ */ from_html(`<section class="accessify-section accessify-section--spotlight"><div class="accessify-section-head"><div><span class="accessify-section-kicker"> </span> <h3> </h3> <p> </p></div></div> <div class="accessify-control-grid"><!> <!></div> <!></section>`);
6092
- var root_10 = /* @__PURE__ */ from_html(`<button class="accessify-card" role="switch"><div class="accessify-card-top"><span class="accessify-card-icon" aria-hidden="true"></span></div> <span class="accessify-card-label"> </span> <span class="accessify-card-desc"> </span></button>`);
6093
- var root_9 = /* @__PURE__ */ from_html(`<section class="accessify-section"><div class="accessify-section-head"><div><h3> </h3> <p> </p></div></div> <div class="accessify-features"></div></section>`);
6094
- var root$2 = /* @__PURE__ */ from_html(`<div class="accessify-body"><!> <!></div>`);
6203
+ var root_2$1 = /* @__PURE__ */ from_html(`<button class="accessify-chip accessify-chip--icon"><span class="accessify-chip-icon" aria-hidden="true"></span> <span> </span></button>`);
6204
+ var root_1$2 = /* @__PURE__ */ from_html(`<div class="accessify-control-row" role="group"><div class="accessify-control-label"><span class="accessify-control-label-icon" aria-hidden="true"></span> <span class="accessify-control-label-text"> </span></div> <div class="accessify-chip-row"></div></div>`);
6205
+ var root_3$1 = /* @__PURE__ */ from_html(`<div class="accessify-control-row" role="group"><div class="accessify-control-label"><span class="accessify-control-label-icon" aria-hidden="true"></span> <span class="accessify-control-label-text"> </span></div> <div class="accessify-stepper"><button class="accessify-stepper-btn" aria-label="Decrease text size">−</button> <span class="accessify-stepper-value" aria-live="polite"> </span> <button class="accessify-stepper-btn" aria-label="Increase text size">+</button></div></div>`);
6206
+ var root_5 = /* @__PURE__ */ from_html(`<div class="accessify-card-wrap"><button class="accessify-card" role="switch"><span class="accessify-card-icon" aria-hidden="true"></span> <span class="accessify-card-label"> </span></button> <span class="accessify-card-info" tabindex="0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg> <span class="accessify-card-tooltip"> </span></span></div>`);
6207
+ var root_4 = /* @__PURE__ */ from_html(`<div class="accessify-features"></div>`);
6208
+ var root$2 = /* @__PURE__ */ from_html(`<!> <!> <div class="accessify-section-divider"></div> <!>`, 1);
6095
6209
  function FeatureGrid($$anchor, $$props) {
6096
6210
  push($$props, true);
6097
6211
  const FEATURE_REGISTRY = {
@@ -6101,12 +6215,6 @@ function FeatureGrid($$anchor, $$props) {
6101
6215
  descKey: "feature.keyboardNav.desc",
6102
6216
  iconId: "keyboard"
6103
6217
  },
6104
- "focus-highlight": {
6105
- id: "focus-highlight",
6106
- nameKey: "feature.focusHighlight",
6107
- descKey: "feature.focusHighlight.desc",
6108
- iconId: "focus"
6109
- },
6110
6218
  "link-highlight": {
6111
6219
  id: "link-highlight",
6112
6220
  nameKey: "feature.linkHighlight",
@@ -6166,72 +6274,57 @@ function FeatureGrid($$anchor, $$props) {
6166
6274
  nameKey: "feature.altText",
6167
6275
  descKey: "feature.altText.desc",
6168
6276
  iconId: "alt-text"
6169
- },
6170
- "auto-scan": {
6171
- id: "auto-scan",
6172
- nameKey: "feature.autoScan",
6173
- descKey: "feature.autoScan.desc",
6174
- iconId: "scan"
6175
6277
  }
6176
6278
  };
6177
- const SECTIONS = [
6279
+ const VISUAL_IDS = [
6280
+ "keyboard-nav",
6281
+ "link-highlight",
6282
+ "animation-stop",
6283
+ "hide-images",
6284
+ "big-cursor"
6285
+ ];
6286
+ const READING_IDS = ["reading-guide", "reading-mask", "tts"];
6287
+ const AI_IDS = ["text-simplify", "alt-text"];
6288
+ const CONTRAST_MODES = [
6178
6289
  {
6179
- id: "essentials",
6180
- labelKey: "section.essentials",
6181
- descKey: "section.essentials.desc",
6182
- featureIds: ["keyboard-nav", "focus-highlight", "link-highlight"]
6290
+ id: "light",
6291
+ labelKey: "contrast.mode.light",
6292
+ icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>'
6183
6293
  },
6184
6294
  {
6185
- id: "support",
6186
- labelKey: "section.support",
6187
- descKey: "section.support.desc",
6188
- featureIds: [
6189
- "reading-guide",
6190
- "reading-mask",
6191
- "animation-stop",
6192
- "hide-images",
6193
- "page-structure",
6194
- "big-cursor",
6195
- "tts"
6196
- ]
6295
+ id: "dark",
6296
+ labelKey: "contrast.mode.dark",
6297
+ icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>'
6197
6298
  },
6198
6299
  {
6199
- id: "ai",
6200
- labelKey: "section.ai",
6201
- descKey: "section.ai.desc",
6202
- featureIds: ["text-simplify", "alt-text", "auto-scan"]
6300
+ id: "high",
6301
+ labelKey: "contrast.mode.high",
6302
+ icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2v20"/><path d="M12 2a10 10 0 0 1 0 20" fill="currentColor"/></svg>'
6203
6303
  }
6204
6304
  ];
6205
- const CONTRAST_MODES = [
6206
- { id: "off", labelKey: "control.default" },
6207
- { id: "light", labelKey: "contrast.mode.light" },
6208
- { id: "dark", labelKey: "contrast.mode.dark" },
6209
- { id: "high", labelKey: "contrast.mode.high" }
6210
- ];
6211
- const TEXT_SIZE_PRESETS = [100, 120, 140, 160, 180];
6305
+ const TEXT_SIZE_MIN = 100;
6306
+ const TEXT_SIZE_MAX = 200;
6307
+ const TEXT_SIZE_STEP = 10;
6212
6308
  const AI_FEATURES = /* @__PURE__ */ new Set(["text-simplify", "alt-text"]);
6213
6309
  const loadedModules = /* @__PURE__ */ new Map();
6214
6310
  const FEATURE_LOADERS = {
6215
6311
  contrast: () => import("./contrast-CqsICAkU.js"),
6216
6312
  "text-size": () => import("./text-size-C6OFhCGi.js"),
6217
- "keyboard-nav": () => import("./keyboard-nav-YtijlLYi.js"),
6218
- "focus-highlight": () => import("./focus-highlight-CjERyyUF.js"),
6313
+ "keyboard-nav": () => import("./keyboard-nav-C9zLmfrJ.js"),
6219
6314
  "link-highlight": () => import("./link-highlight-DBGm067Y.js"),
6220
6315
  "reading-guide": () => import("./reading-guide-VT8NciIL.js"),
6221
6316
  "reading-mask": () => import("./reading-mask-BABChuCz.js"),
6222
6317
  "animation-stop": () => import("./animation-stop-DXebPS8D.js"),
6223
6318
  "hide-images": () => import("./hide-images-DJwmsV2C.js"),
6224
6319
  "big-cursor": () => import("./big-cursor-B2UKu9dQ.js"),
6225
- "page-structure": () => import("./page-structure-WFqy5QjQ.js"),
6226
- tts: () => import("./tts-02b9iV0h.js"),
6227
- "text-simplify": () => import("./text-simplify-CELklw5A.js"),
6228
- "alt-text": () => import("./alt-text-CrPRUX83.js"),
6229
- "auto-scan": () => import("./auto-scan-pg-09o7A.js")
6320
+ "page-structure": () => import("./page-structure-GqhCQsl3.js"),
6321
+ tts: () => import("./tts-CjszLRnb.js"),
6322
+ "text-simplify": () => import("./text-simplify-C9gzE3t0.js"),
6323
+ "alt-text": () => Promise.resolve().then(() => altText)
6230
6324
  };
6231
6325
  let contrastMode = /* @__PURE__ */ state(proxy(readStoredContrastMode()));
6232
6326
  let textSize = /* @__PURE__ */ state(proxy(readStoredTextSize()));
6233
6327
  let visibleFeatureIds = /* @__PURE__ */ user_derived(() => new Set($$props.config.features));
6234
- let essentialFeatures = /* @__PURE__ */ user_derived(() => getFeatureDefs(SECTIONS[0].featureIds));
6235
6328
  let activeSignature = /* @__PURE__ */ user_derived(() => Array.from($$props.activeFeatures.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([id, enabled]) => `${id}:${enabled}`).join("|"));
6236
6329
  let aiService;
6237
6330
  function readStoredContrastMode() {
@@ -6258,7 +6351,7 @@ function FeatureGrid($$anchor, $$props) {
6258
6351
  const iconFn = icons[iconId];
6259
6352
  return iconFn ? iconFn() : "";
6260
6353
  }
6261
- function getFeatureDefs(ids) {
6354
+ function getVisibleFeatures(ids) {
6262
6355
  return ids.filter((id) => get(visibleFeatureIds).has(id)).map((id) => FEATURE_REGISTRY[id]).filter(Boolean);
6263
6356
  }
6264
6357
  function updateControlState(id, module) {
@@ -6291,7 +6384,10 @@ function FeatureGrid($$anchor, $$props) {
6291
6384
  try {
6292
6385
  const mod = await loader();
6293
6386
  const exported = mod.default ?? mod;
6294
- const featureModule = typeof exported === "function" ? AI_FEATURES.has(id) ? exported(getAIService(), $$props.config.lang) : exported() : exported;
6387
+ const featureModule = typeof exported === "function" ? AI_FEATURES.has(id) ? exported(getAIService(), $$props.config.lang, {
6388
+ siteKey: $$props.config.siteKey,
6389
+ proxyUrl: $$props.config.proxyUrl
6390
+ }) : exported() : exported;
6295
6391
  loadedModules.set(id, featureModule);
6296
6392
  return featureModule;
6297
6393
  } catch (error) {
@@ -6356,251 +6452,165 @@ function FeatureGrid($$anchor, $$props) {
6356
6452
  set(textSize, value, true);
6357
6453
  $$props.ontoggle("text-size", true);
6358
6454
  }
6455
+ let visualFeatures = /* @__PURE__ */ user_derived(() => getVisibleFeatures(VISUAL_IDS));
6456
+ let readingFeatures = /* @__PURE__ */ user_derived(() => getVisibleFeatures(READING_IDS));
6457
+ let aiFeatures = /* @__PURE__ */ user_derived(() => getVisibleFeatures(AI_IDS));
6359
6458
  user_effect(() => {
6360
6459
  `${$$props.config.features.join("|")}|${get(activeSignature)}`;
6361
6460
  void syncActiveModules();
6362
6461
  });
6363
- var div = root$2();
6364
- var node = child(div);
6462
+ var fragment = root$2();
6463
+ var node = first_child(fragment);
6365
6464
  {
6366
- var consequent_3 = ($$anchor2) => {
6367
- var section_1 = root_1$1();
6368
- var div_1 = child(section_1);
6369
- var div_2 = child(div_1);
6370
- var span = child(div_2);
6371
- var text = child(span);
6372
- var h3 = sibling(span, 2);
6373
- var text_1 = child(h3);
6374
- var p = sibling(h3, 2);
6375
- var text_2 = child(p);
6376
- var div_3 = sibling(div_1, 2);
6377
- var node_1 = child(div_3);
6378
- {
6379
- var consequent = ($$anchor3) => {
6380
- var article = root_2();
6381
- var div_4 = child(article);
6382
- var span_1 = child(div_4);
6383
- html(span_1, () => getIcon("contrast"), true);
6384
- var div_5 = sibling(span_1, 2);
6385
- var h4 = child(div_5);
6386
- var text_3 = child(h4);
6387
- var p_1 = sibling(h4, 2);
6388
- var text_4 = child(p_1);
6389
- var div_6 = sibling(div_4, 2);
6390
- each(div_6, 21, () => CONTRAST_MODES, index, ($$anchor4, mode) => {
6391
- var button = root_3();
6392
- var text_5 = child(button);
6393
- template_effect(
6394
- ($0) => {
6395
- set_attribute(button, "data-active", get(contrastMode) === get(mode).id);
6396
- set_attribute(button, "aria-pressed", get(contrastMode) === get(mode).id);
6397
- set_text(text_5, $0);
6398
- },
6399
- [() => t(get(mode).labelKey, $$props.config.lang)]
6400
- );
6401
- delegated("click", button, () => setContrastMode(get(mode).id));
6402
- append($$anchor4, button);
6403
- });
6404
- template_effect(
6405
- ($0, $1, $2) => {
6406
- set_text(text_3, $0);
6407
- set_text(text_4, $1);
6408
- set_attribute(div_6, "aria-label", $2);
6409
- },
6410
- [
6411
- () => t("feature.contrast", $$props.config.lang),
6412
- () => t("feature.contrast.desc", $$props.config.lang),
6413
- () => t("control.chooseMode", $$props.config.lang)
6414
- ]
6415
- );
6416
- append($$anchor3, article);
6417
- };
6418
- var d = /* @__PURE__ */ user_derived(() => get(visibleFeatureIds).has("contrast"));
6419
- if_block(node_1, ($$render) => {
6420
- if (get(d)) $$render(consequent);
6421
- });
6422
- }
6423
- var node_2 = sibling(node_1, 2);
6424
- {
6425
- var consequent_1 = ($$anchor3) => {
6426
- var article_1 = root_4();
6427
- var div_7 = child(article_1);
6428
- var span_2 = child(div_7);
6429
- html(span_2, () => getIcon("text-size"), true);
6430
- var div_8 = sibling(span_2, 2);
6431
- var h4_1 = child(div_8);
6432
- var text_6 = child(h4_1);
6433
- var p_2 = sibling(h4_1, 2);
6434
- var text_7 = child(p_2);
6435
- var div_9 = sibling(div_7, 2);
6436
- each(div_9, 21, () => TEXT_SIZE_PRESETS, index, ($$anchor4, preset) => {
6437
- var button_1 = root_5();
6438
- var text_8 = child(button_1);
6439
- template_effect(() => {
6440
- set_attribute(button_1, "data-active", get(textSize) === get(preset));
6441
- set_attribute(button_1, "aria-pressed", get(textSize) === get(preset));
6442
- set_text(text_8, `${get(preset) ?? ""}%`);
6443
- });
6444
- delegated("click", button_1, () => setTextSize(get(preset)));
6445
- append($$anchor4, button_1);
6446
- });
6447
- template_effect(
6448
- ($0, $1, $2) => {
6449
- set_text(text_6, $0);
6450
- set_text(text_7, $1);
6451
- set_attribute(div_9, "aria-label", $2);
6452
- },
6453
- [
6454
- () => t("feature.textSize", $$props.config.lang),
6455
- () => t("feature.textSize.desc", $$props.config.lang),
6456
- () => t("feature.textSize", $$props.config.lang)
6457
- ]
6458
- );
6459
- append($$anchor3, article_1);
6460
- };
6461
- var d_1 = /* @__PURE__ */ user_derived(() => get(visibleFeatureIds).has("text-size"));
6462
- if_block(node_2, ($$render) => {
6463
- if (get(d_1)) $$render(consequent_1);
6464
- });
6465
- }
6466
- var node_3 = sibling(div_3, 2);
6467
- {
6468
- var consequent_2 = ($$anchor3) => {
6469
- var div_10 = root_6();
6470
- each(div_10, 21, () => get(essentialFeatures), (feature) => feature.id, ($$anchor4, feature) => {
6471
- const isActive = /* @__PURE__ */ user_derived(() => $$props.activeFeatures.get(get(feature).id) ?? false);
6472
- var button_2 = root_7();
6473
- var div_11 = child(button_2);
6474
- var span_3 = child(div_11);
6475
- html(span_3, () => getIcon(get(feature).iconId), true);
6476
- var span_4 = sibling(div_11, 2);
6477
- var text_9 = child(span_4);
6478
- var span_5 = sibling(span_4, 2);
6479
- var text_10 = child(span_5);
6480
- template_effect(
6481
- ($0, $1, $2) => {
6482
- set_attribute(button_2, "data-active", get(isActive));
6483
- set_attribute(button_2, "aria-checked", get(isActive));
6484
- set_attribute(button_2, "aria-label", $0);
6485
- set_text(text_9, $1);
6486
- set_text(text_10, $2);
6487
- },
6488
- [
6489
- () => t(get(feature).nameKey, $$props.config.lang),
6490
- () => t(get(feature).nameKey, $$props.config.lang),
6491
- () => t(get(feature).descKey, $$props.config.lang)
6492
- ]
6493
- );
6494
- delegated("click", button_2, () => handleToggle(get(feature).id));
6495
- append($$anchor4, button_2);
6496
- });
6497
- append($$anchor3, div_10);
6498
- };
6499
- if_block(node_3, ($$render) => {
6500
- if (get(essentialFeatures).length > 0) $$render(consequent_2);
6501
- });
6502
- }
6465
+ var consequent = ($$anchor2) => {
6466
+ var div = root_1$2();
6467
+ var div_1 = child(div);
6468
+ var span = child(div_1);
6469
+ html(span, () => getIcon("contrast"), true);
6470
+ var span_1 = sibling(span, 2);
6471
+ var text = child(span_1);
6472
+ var div_2 = sibling(div_1, 2);
6473
+ each(div_2, 21, () => CONTRAST_MODES, index, ($$anchor3, mode) => {
6474
+ var button = root_2$1();
6475
+ var span_2 = child(button);
6476
+ html(span_2, () => get(mode).icon, true);
6477
+ var span_3 = sibling(span_2, 2);
6478
+ var text_1 = child(span_3);
6479
+ template_effect(
6480
+ ($0) => {
6481
+ set_attribute(button, "data-active", get(contrastMode) === get(mode).id);
6482
+ set_attribute(button, "aria-pressed", get(contrastMode) === get(mode).id);
6483
+ set_text(text_1, $0);
6484
+ },
6485
+ [() => t(get(mode).labelKey, $$props.config.lang)]
6486
+ );
6487
+ delegated("click", button, () => setContrastMode(get(contrastMode) === get(mode).id ? "off" : get(mode).id));
6488
+ append($$anchor3, button);
6489
+ });
6503
6490
  template_effect(
6504
- ($0, $1, $2) => {
6505
- set_text(text, $0);
6506
- set_text(text_1, $1);
6507
- set_text(text_2, $2);
6491
+ ($0, $1) => {
6492
+ set_attribute(div, "aria-label", $0);
6493
+ set_text(text, $1);
6508
6494
  },
6509
6495
  [
6510
- () => t("control.mostUsed", $$props.config.lang),
6511
- () => t(SECTIONS[0].labelKey, $$props.config.lang),
6512
- () => t(SECTIONS[0].descKey, $$props.config.lang)
6496
+ () => t("feature.contrast", $$props.config.lang),
6497
+ () => t("feature.contrast", $$props.config.lang)
6513
6498
  ]
6514
6499
  );
6515
- append($$anchor2, section_1);
6500
+ append($$anchor2, div);
6516
6501
  };
6517
- var d_2 = /* @__PURE__ */ user_derived(() => get(visibleFeatureIds).has("contrast") || get(visibleFeatureIds).has("text-size") || getFeatureDefs(SECTIONS[0].featureIds).length > 0);
6502
+ var d = /* @__PURE__ */ user_derived(() => get(visibleFeatureIds).has("contrast"));
6518
6503
  if_block(node, ($$render) => {
6519
- if (get(d_2)) $$render(consequent_3);
6504
+ if (get(d)) $$render(consequent);
6520
6505
  });
6521
6506
  }
6522
- var node_4 = sibling(node, 2);
6523
- each(node_4, 17, () => SECTIONS.slice(1), index, ($$anchor2, section) => {
6524
- const features = /* @__PURE__ */ user_derived(() => getFeatureDefs(get(section).featureIds));
6525
- var fragment = comment();
6526
- var node_5 = first_child(fragment);
6527
- {
6528
- var consequent_4 = ($$anchor3) => {
6529
- var section_2 = root_9();
6530
- var div_12 = child(section_2);
6531
- var div_13 = child(div_12);
6532
- var h3_1 = child(div_13);
6533
- var text_11 = child(h3_1);
6534
- var p_3 = sibling(h3_1, 2);
6535
- var text_12 = child(p_3);
6536
- var div_14 = sibling(div_12, 2);
6537
- each(div_14, 21, () => get(features), (feature) => feature.id, ($$anchor4, feature) => {
6507
+ var node_1 = sibling(node, 2);
6508
+ {
6509
+ var consequent_1 = ($$anchor2) => {
6510
+ var div_3 = root_3$1();
6511
+ var div_4 = child(div_3);
6512
+ var span_4 = child(div_4);
6513
+ html(span_4, () => getIcon("text-size"), true);
6514
+ var span_5 = sibling(span_4, 2);
6515
+ var text_2 = child(span_5);
6516
+ var div_5 = sibling(div_4, 2);
6517
+ var button_1 = child(div_5);
6518
+ var span_6 = sibling(button_1, 2);
6519
+ var text_3 = child(span_6);
6520
+ var button_2 = sibling(span_6, 2);
6521
+ template_effect(
6522
+ ($0, $1) => {
6523
+ set_attribute(div_3, "aria-label", $0);
6524
+ set_text(text_2, $1);
6525
+ button_1.disabled = get(textSize) <= TEXT_SIZE_MIN;
6526
+ set_text(text_3, `${get(textSize) ?? ""}%`);
6527
+ button_2.disabled = get(textSize) >= TEXT_SIZE_MAX;
6528
+ },
6529
+ [
6530
+ () => t("feature.textSize", $$props.config.lang),
6531
+ () => t("feature.textSize", $$props.config.lang)
6532
+ ]
6533
+ );
6534
+ delegated("click", button_1, () => setTextSize(Math.max(TEXT_SIZE_MIN, get(textSize) - TEXT_SIZE_STEP)));
6535
+ delegated("click", button_2, () => setTextSize(Math.min(TEXT_SIZE_MAX, get(textSize) + TEXT_SIZE_STEP)));
6536
+ append($$anchor2, div_3);
6537
+ };
6538
+ var d_1 = /* @__PURE__ */ user_derived(() => get(visibleFeatureIds).has("text-size"));
6539
+ if_block(node_1, ($$render) => {
6540
+ if (get(d_1)) $$render(consequent_1);
6541
+ });
6542
+ }
6543
+ var node_2 = sibling(node_1, 4);
6544
+ {
6545
+ var consequent_2 = ($$anchor2) => {
6546
+ var div_6 = root_4();
6547
+ each(
6548
+ div_6,
6549
+ 21,
6550
+ () => [
6551
+ ...get(visualFeatures),
6552
+ ...get(readingFeatures),
6553
+ ...get(aiFeatures)
6554
+ ],
6555
+ (feature) => feature.id,
6556
+ ($$anchor3, feature) => {
6538
6557
  const isActive = /* @__PURE__ */ user_derived(() => $$props.activeFeatures.get(get(feature).id) ?? false);
6539
- var button_3 = root_10();
6540
- var div_15 = child(button_3);
6541
- var span_6 = child(div_15);
6542
- html(span_6, () => getIcon(get(feature).iconId), true);
6543
- var span_7 = sibling(div_15, 2);
6544
- var text_13 = child(span_7);
6558
+ var div_7 = root_5();
6559
+ var button_3 = child(div_7);
6560
+ var span_7 = child(button_3);
6561
+ html(span_7, () => getIcon(get(feature).iconId), true);
6545
6562
  var span_8 = sibling(span_7, 2);
6546
- var text_14 = child(span_8);
6563
+ var text_4 = child(span_8);
6564
+ var span_9 = sibling(button_3, 2);
6565
+ var span_10 = sibling(child(span_9), 2);
6566
+ var text_5 = child(span_10);
6547
6567
  template_effect(
6548
- ($0, $1, $2) => {
6568
+ ($0, $1, $2, $3) => {
6549
6569
  set_attribute(button_3, "data-active", get(isActive));
6550
6570
  set_attribute(button_3, "aria-checked", get(isActive));
6551
6571
  set_attribute(button_3, "aria-label", $0);
6552
- set_text(text_13, $1);
6553
- set_text(text_14, $2);
6572
+ set_text(text_4, $1);
6573
+ set_attribute(span_9, "aria-label", $2);
6574
+ set_text(text_5, $3);
6554
6575
  },
6555
6576
  [
6556
6577
  () => t(get(feature).nameKey, $$props.config.lang),
6557
6578
  () => t(get(feature).nameKey, $$props.config.lang),
6579
+ () => t(get(feature).descKey, $$props.config.lang),
6558
6580
  () => t(get(feature).descKey, $$props.config.lang)
6559
6581
  ]
6560
6582
  );
6561
6583
  delegated("click", button_3, () => handleToggle(get(feature).id));
6562
- append($$anchor4, button_3);
6563
- });
6564
- template_effect(
6565
- ($0, $1) => {
6566
- set_text(text_11, $0);
6567
- set_text(text_12, $1);
6568
- },
6569
- [
6570
- () => t(get(section).labelKey, $$props.config.lang),
6571
- () => t(get(section).descKey, $$props.config.lang)
6572
- ]
6573
- );
6574
- append($$anchor3, section_2);
6575
- };
6576
- if_block(node_5, ($$render) => {
6577
- if (get(features).length > 0) $$render(consequent_4);
6578
- });
6579
- }
6580
- append($$anchor2, fragment);
6581
- });
6582
- append($$anchor, div);
6584
+ append($$anchor3, div_7);
6585
+ }
6586
+ );
6587
+ append($$anchor2, div_6);
6588
+ };
6589
+ if_block(node_2, ($$render) => {
6590
+ if (get(visualFeatures).length > 0 || get(readingFeatures).length > 0 || get(aiFeatures).length > 0) $$render(consequent_2);
6591
+ });
6592
+ }
6593
+ append($$anchor, fragment);
6583
6594
  pop();
6584
6595
  }
6585
6596
  delegate(["click"]);
6586
- var root_1 = /* @__PURE__ */ from_html(`<span aria-hidden="true">&middot;</span> <a target="_blank" rel="noopener noreferrer"> </a>`, 1);
6587
- var root$1 = /* @__PURE__ */ from_html(`<div class="accessify-footer"><span> </span> <!></div>`);
6597
+ var root_1$1 = /* @__PURE__ */ from_html(`<span aria-hidden="true">&middot;</span> <a target="_blank" rel="noopener noreferrer"> </a>`, 1);
6598
+ var root$1 = /* @__PURE__ */ from_html(`<div class="accessify-footer"><a target="_blank" rel="noopener noreferrer">Accessify Dashboard</a> <!></div>`);
6588
6599
  function PanelFooter($$anchor, $$props) {
6589
6600
  push($$props, true);
6590
6601
  let lang = prop($$props, "lang", 3, "en");
6591
6602
  var div = root$1();
6592
- var span = child(div);
6593
- var text = child(span);
6594
- var node = sibling(span, 2);
6603
+ var a = child(div);
6604
+ var node = sibling(a, 2);
6595
6605
  {
6596
6606
  var consequent = ($$anchor2) => {
6597
- var fragment = root_1();
6598
- var a = sibling(first_child(fragment), 2);
6599
- var text_1 = child(a);
6607
+ var fragment = root_1$1();
6608
+ var a_1 = sibling(first_child(fragment), 2);
6609
+ var text = child(a_1);
6600
6610
  template_effect(
6601
6611
  ($0) => {
6602
- set_attribute(a, "href", $$props.statementUrl);
6603
- set_text(text_1, $0);
6612
+ set_attribute(a_1, "href", $$props.statementUrl);
6613
+ set_text(text, $0);
6604
6614
  },
6605
6615
  [() => t("widget.statement", lang())]
6606
6616
  );
@@ -6610,48 +6620,186 @@ function PanelFooter($$anchor, $$props) {
6610
6620
  if ($$props.statementUrl) $$render(consequent);
6611
6621
  });
6612
6622
  }
6613
- template_effect(($0) => set_text(text, $0), [() => t("widget.poweredBy", lang())]);
6623
+ template_effect(() => set_attribute(a, "href", $$props.dashboardUrl || "https://accessify-dashboard.pages.dev"));
6614
6624
  append($$anchor, div);
6615
6625
  pop();
6616
6626
  }
6617
- var root = /* @__PURE__ */ from_html(`<div class="sr-only" role="status" aria-live="polite" aria-atomic="true"> </div> <!> <div class="accessify-panel" role="dialog"><!> <!> <!> <!></div>`, 1);
6618
- function WidgetApp($$anchor, $$props) {
6619
- push($$props, true);
6620
- let config2 = prop($$props, "config", 7);
6621
- let isOpen = /* @__PURE__ */ state(false);
6622
- let activeFeatures = /* @__PURE__ */ state(proxy(/* @__PURE__ */ new Map()));
6623
- let currentTheme = /* @__PURE__ */ state("light");
6624
- let panelEl = /* @__PURE__ */ state(null);
6625
- let triggerEl = /* @__PURE__ */ state(null);
6626
- let announcement = /* @__PURE__ */ state("");
6627
- let widgetLang = /* @__PURE__ */ state("en");
6628
- const STORAGE_KEY = "accessify-prefs";
6629
- let activeCount = /* @__PURE__ */ user_derived(() => Array.from(get(activeFeatures).values()).filter(Boolean).length);
6630
- function announce(message) {
6631
- set(announcement, "");
6632
- requestAnimationFrame(() => {
6633
- set(announcement, message, true);
6634
- });
6635
- }
6636
- function loadPrefs() {
6637
- try {
6638
- const saved = localStorage.getItem(STORAGE_KEY);
6639
- if (saved) {
6640
- const prefs = JSON.parse(saved);
6641
- set(activeFeatures, new Map(Object.entries(prefs)), true);
6627
+ let savedNodes = [];
6628
+ let currentTranslateLang = null;
6629
+ let translating = false;
6630
+ const TRANSLATE_URL = "https://translate.googleapis.com/translate_a/single";
6631
+ async function translateSingleText(text, sourceLang, targetLang) {
6632
+ const params = new URLSearchParams({
6633
+ client: "gtx",
6634
+ sl: sourceLang,
6635
+ tl: targetLang,
6636
+ dt: "t",
6637
+ q: text
6638
+ });
6639
+ try {
6640
+ const res = await fetch(`${TRANSLATE_URL}?${params.toString()}`);
6641
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
6642
+ const data = await res.json();
6643
+ if (Array.isArray(data[0])) {
6644
+ const parts = [];
6645
+ for (const segment of data[0]) {
6646
+ if (Array.isArray(segment) && typeof segment[0] === "string") {
6647
+ parts.push(segment[0]);
6648
+ }
6642
6649
  }
6643
- } catch {
6650
+ return parts.join("");
6644
6651
  }
6652
+ return text;
6653
+ } catch {
6654
+ return text;
6645
6655
  }
6646
- function savePrefs() {
6647
- try {
6648
- const obj = Object.fromEntries(get(activeFeatures));
6649
- localStorage.setItem(STORAGE_KEY, JSON.stringify(obj));
6650
- } catch {
6656
+ }
6657
+ async function translateTexts(texts, sourceLang, targetLang) {
6658
+ const CONCURRENCY = 10;
6659
+ const results = new Array(texts.length);
6660
+ for (let start = 0; start < texts.length; start += CONCURRENCY) {
6661
+ const batch = texts.slice(start, start + CONCURRENCY);
6662
+ const translated = await Promise.all(
6663
+ batch.map((text) => translateSingleText(text, sourceLang, targetLang))
6664
+ );
6665
+ for (let j = 0; j < translated.length; j++) {
6666
+ results[start + j] = translated[j];
6651
6667
  }
6652
6668
  }
6653
- function detectTheme() {
6654
- if (config2().theme === "light" || config2().theme === "dark") {
6669
+ return results;
6670
+ }
6671
+ function getTranslatableTextNodes() {
6672
+ const walker = document.createTreeWalker(
6673
+ document.body,
6674
+ NodeFilter.SHOW_TEXT,
6675
+ {
6676
+ acceptNode(node) {
6677
+ if (!node.textContent || !node.textContent.trim()) {
6678
+ return NodeFilter.FILTER_REJECT;
6679
+ }
6680
+ const parent = node.parentElement;
6681
+ if (!parent) return NodeFilter.FILTER_REJECT;
6682
+ const tag2 = parent.tagName;
6683
+ if (["SCRIPT", "STYLE", "NOSCRIPT", "CODE", "PRE", "TEXTAREA", "INPUT"].includes(tag2)) {
6684
+ return NodeFilter.FILTER_REJECT;
6685
+ }
6686
+ if (parent.closest("accessify-widget")) {
6687
+ return NodeFilter.FILTER_REJECT;
6688
+ }
6689
+ return NodeFilter.FILTER_ACCEPT;
6690
+ }
6691
+ }
6692
+ );
6693
+ const nodes = [];
6694
+ let current;
6695
+ while (current = walker.nextNode()) {
6696
+ nodes.push(current);
6697
+ }
6698
+ return nodes;
6699
+ }
6700
+ function createTextTransformService() {
6701
+ return {
6702
+ async translate(targetLang) {
6703
+ if (translating) return;
6704
+ translating = true;
6705
+ try {
6706
+ if (savedNodes.length > 0) {
6707
+ this.restore();
6708
+ }
6709
+ const pageLang = document.documentElement.lang?.split("-")[0] || "auto";
6710
+ const textNodes = getTranslatableTextNodes();
6711
+ if (textNodes.length === 0) {
6712
+ translating = false;
6713
+ return;
6714
+ }
6715
+ savedNodes = textNodes.map((node) => ({
6716
+ node,
6717
+ original: node.textContent
6718
+ }));
6719
+ const uniqueTexts = [...new Set(savedNodes.map((s) => s.original.trim()).filter(Boolean))];
6720
+ const translated = await translateTexts(uniqueTexts, pageLang, targetLang);
6721
+ const lookup = /* @__PURE__ */ new Map();
6722
+ uniqueTexts.forEach((text, i) => {
6723
+ if (translated[i]) {
6724
+ lookup.set(text, translated[i]);
6725
+ }
6726
+ });
6727
+ for (const saved of savedNodes) {
6728
+ const trimmed = saved.original.trim();
6729
+ const replacement = lookup.get(trimmed);
6730
+ if (replacement) {
6731
+ const leading = saved.original.match(/^\s*/)?.[0] || "";
6732
+ const trailing = saved.original.match(/\s*$/)?.[0] || "";
6733
+ saved.node.textContent = leading + replacement + trailing;
6734
+ }
6735
+ }
6736
+ currentTranslateLang = targetLang;
6737
+ } finally {
6738
+ translating = false;
6739
+ }
6740
+ },
6741
+ async simplify(_level, _lang) {
6742
+ },
6743
+ restore() {
6744
+ for (const saved of savedNodes) {
6745
+ saved.node.textContent = saved.original;
6746
+ }
6747
+ savedNodes = [];
6748
+ currentTranslateLang = null;
6749
+ },
6750
+ isTranslated() {
6751
+ return currentTranslateLang !== null;
6752
+ },
6753
+ currentLang() {
6754
+ return currentTranslateLang;
6755
+ }
6756
+ };
6757
+ }
6758
+ var root_3 = /* @__PURE__ */ from_svg(`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"></path></svg>`);
6759
+ var root_2 = /* @__PURE__ */ from_html(`<button class="accessify-lang-option" role="option"><span> </span> <!></button>`);
6760
+ var root_1 = /* @__PURE__ */ from_html(`<div class="accessify-lang-list" role="listbox"></div>`);
6761
+ var root = /* @__PURE__ */ from_html(`<div class="sr-only" role="status" aria-live="polite" aria-atomic="true"> </div> <!> <div class="accessify-panel" role="dialog"><!> <div class="accessify-body"><div class="accessify-lang-dropdown"><button class="accessify-lang-toggle"><span class="accessify-lang-toggle-left"><!> <span> </span></span> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <!></div> <div class="accessify-section-divider"></div> <!> <div class="accessify-section-divider"></div> <!></div> <!></div>`, 1);
6762
+ function WidgetApp($$anchor, $$props) {
6763
+ push($$props, true);
6764
+ let config2 = prop($$props, "config", 7);
6765
+ let isOpen = /* @__PURE__ */ state(false);
6766
+ let activeFeatures = /* @__PURE__ */ state(proxy(/* @__PURE__ */ new Map()));
6767
+ let currentTheme = /* @__PURE__ */ state("light");
6768
+ let panelEl = /* @__PURE__ */ state(null);
6769
+ let triggerEl = /* @__PURE__ */ state(null);
6770
+ let announcement = /* @__PURE__ */ state("");
6771
+ let widgetLang = /* @__PURE__ */ state("en");
6772
+ let activeProfileId = /* @__PURE__ */ state(null);
6773
+ let langDropdownOpen = /* @__PURE__ */ state(false);
6774
+ const textTransformService = createTextTransformService();
6775
+ const STORAGE_KEY = "accessify-prefs";
6776
+ let activeCount = /* @__PURE__ */ user_derived(() => Array.from(get(activeFeatures).values()).filter(Boolean).length);
6777
+ let currentLangOption = /* @__PURE__ */ user_derived(() => LANGUAGE_OPTIONS.find((o) => o.code === get(widgetLang)));
6778
+ function announce(message) {
6779
+ set(announcement, "");
6780
+ requestAnimationFrame(() => {
6781
+ set(announcement, message, true);
6782
+ });
6783
+ }
6784
+ function loadPrefs() {
6785
+ try {
6786
+ const saved = localStorage.getItem(STORAGE_KEY);
6787
+ if (saved) {
6788
+ const prefs = JSON.parse(saved);
6789
+ set(activeFeatures, new Map(Object.entries(prefs)), true);
6790
+ }
6791
+ } catch {
6792
+ }
6793
+ }
6794
+ function savePrefs() {
6795
+ try {
6796
+ const obj = Object.fromEntries(get(activeFeatures));
6797
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(obj));
6798
+ } catch {
6799
+ }
6800
+ }
6801
+ function detectTheme() {
6802
+ if (config2().theme === "light" || config2().theme === "dark") {
6655
6803
  set(currentTheme, config2().theme, true);
6656
6804
  return;
6657
6805
  }
@@ -6711,14 +6859,43 @@ function WidgetApp($$anchor, $$props) {
6711
6859
  }
6712
6860
  function resetAll() {
6713
6861
  set(activeFeatures, /* @__PURE__ */ new Map(), true);
6862
+ set(activeProfileId, null);
6714
6863
  savePrefs();
6864
+ textTransformService.restore();
6715
6865
  window.dispatchEvent(new CustomEvent("accessify:reset"));
6716
6866
  announce(t("widget.resetConfirm", get(widgetLang)));
6717
6867
  }
6868
+ function handleProfileActivate(profile) {
6869
+ if (get(activeProfileId) === profile.id) {
6870
+ set(activeProfileId, null);
6871
+ for (const fid of profile.features) {
6872
+ get(activeFeatures).set(fid, false);
6873
+ }
6874
+ set(activeFeatures, new Map(get(activeFeatures)), true);
6875
+ savePrefs();
6876
+ window.dispatchEvent(new CustomEvent("accessify:reset"));
6877
+ announce(`${t(profile.nameKey, get(widgetLang))} ${t("status.deactivated", get(widgetLang))}`);
6878
+ return;
6879
+ }
6880
+ set(activeProfileId, profile.id, true);
6881
+ for (const fid of profile.features) {
6882
+ get(activeFeatures).set(fid, true);
6883
+ }
6884
+ set(activeFeatures, new Map(get(activeFeatures)), true);
6885
+ savePrefs();
6886
+ announce(`${t(profile.nameKey, get(widgetLang))} ${t("status.activated", get(widgetLang))}`);
6887
+ }
6718
6888
  function handleLangChange(newLang) {
6719
6889
  set(widgetLang, getSupportedLang(newLang), true);
6720
6890
  config2().lang = get(widgetLang);
6891
+ set(langDropdownOpen, false);
6721
6892
  applyLanguage();
6893
+ const pageLang = document.documentElement.lang?.split("-")[0] || "en";
6894
+ if (get(widgetLang) !== pageLang) {
6895
+ textTransformService.translate(get(widgetLang));
6896
+ } else {
6897
+ textTransformService.restore();
6898
+ }
6722
6899
  }
6723
6900
  function handleKeydown(e) {
6724
6901
  if (e.key === "Escape" && get(isOpen)) {
@@ -6775,24 +6952,69 @@ function WidgetApp($$anchor, $$props) {
6775
6952
  get lang() {
6776
6953
  return get(widgetLang);
6777
6954
  },
6778
- onclose: close,
6779
- onreset: resetAll,
6780
- onlangchange: handleLangChange
6781
- });
6782
- var node_2 = sibling(node_1, 2);
6783
- StatusBar(node_2, {
6784
6955
  get activeCount() {
6785
6956
  return get(activeCount);
6786
6957
  },
6958
+ onclose: close,
6959
+ onreset: resetAll
6960
+ });
6961
+ var div_2 = sibling(node_1, 2);
6962
+ var div_3 = child(div_2);
6963
+ var button = child(div_3);
6964
+ var span = child(button);
6965
+ var node_2 = child(span);
6966
+ html(node_2, iconGlobe);
6967
+ var span_1 = sibling(node_2, 2);
6968
+ var text_1 = child(span_1);
6969
+ var svg = sibling(span, 2);
6970
+ let classes;
6971
+ var node_3 = sibling(button, 2);
6972
+ {
6973
+ var consequent_1 = ($$anchor2) => {
6974
+ var div_4 = root_1();
6975
+ each(div_4, 21, () => LANGUAGE_OPTIONS, index, ($$anchor3, option) => {
6976
+ var button_1 = root_2();
6977
+ var span_2 = child(button_1);
6978
+ var text_2 = child(span_2);
6979
+ var node_4 = sibling(span_2, 2);
6980
+ {
6981
+ var consequent = ($$anchor4) => {
6982
+ var svg_1 = root_3();
6983
+ append($$anchor4, svg_1);
6984
+ };
6985
+ if_block(node_4, ($$render) => {
6986
+ if (get(widgetLang) === get(option).code) $$render(consequent);
6987
+ });
6988
+ }
6989
+ template_effect(() => {
6990
+ set_attribute(button_1, "aria-selected", get(widgetLang) === get(option).code);
6991
+ set_attribute(button_1, "data-active", get(widgetLang) === get(option).code);
6992
+ set_text(text_2, `${(get(option).flag || "") ?? ""} ${get(option).label ?? ""}`);
6993
+ });
6994
+ delegated("click", button_1, () => handleLangChange(get(option).code));
6995
+ append($$anchor3, button_1);
6996
+ });
6997
+ template_effect(($0) => set_attribute(div_4, "aria-label", $0), [() => t("widget.widgetLang", get(widgetLang))]);
6998
+ append($$anchor2, div_4);
6999
+ };
7000
+ if_block(node_3, ($$render) => {
7001
+ if (get(langDropdownOpen)) $$render(consequent_1);
7002
+ });
7003
+ }
7004
+ var node_5 = sibling(div_3, 4);
7005
+ ProfileSection(node_5, {
6787
7006
  get lang() {
6788
7007
  return get(widgetLang);
6789
7008
  },
6790
- onreset: resetAll
7009
+ get activeProfileId() {
7010
+ return get(activeProfileId);
7011
+ },
7012
+ onactivate: handleProfileActivate
6791
7013
  });
6792
- var node_3 = sibling(node_2, 2);
7014
+ var node_6 = sibling(node_5, 4);
6793
7015
  {
6794
7016
  let $0 = /* @__PURE__ */ user_derived(() => ({ ...config2(), lang: get(widgetLang) }));
6795
- FeatureGrid(node_3, {
7017
+ FeatureGrid(node_6, {
6796
7018
  get config() {
6797
7019
  return get($0);
6798
7020
  },
@@ -6802,13 +7024,16 @@ function WidgetApp($$anchor, $$props) {
6802
7024
  ontoggle: handleFeatureToggle
6803
7025
  });
6804
7026
  }
6805
- var node_4 = sibling(node_3, 2);
6806
- PanelFooter(node_4, {
7027
+ var node_7 = sibling(div_2, 2);
7028
+ PanelFooter(node_7, {
6807
7029
  get lang() {
6808
7030
  return get(widgetLang);
6809
7031
  },
6810
7032
  get statementUrl() {
6811
7033
  return config2().statementUrl;
7034
+ },
7035
+ get dashboardUrl() {
7036
+ return config2().dashboardUrl;
6812
7037
  }
6813
7038
  });
6814
7039
  bind_this(div_1, ($$value) => set(panelEl, $$value), () => get(panelEl));
@@ -6818,6 +7043,9 @@ function WidgetApp($$anchor, $$props) {
6818
7043
  set_attribute(div_1, "aria-label", $0);
6819
7044
  set_attribute(div_1, "aria-hidden", !get(isOpen));
6820
7045
  set_attribute(div_1, "dir", $1);
7046
+ set_attribute(button, "aria-expanded", get(langDropdownOpen));
7047
+ set_text(text_1, `${(get(currentLangOption)?.flag || "") ?? ""} ${(get(currentLangOption)?.label || get(widgetLang)) ?? ""}`);
7048
+ classes = set_class(svg, 0, "accessify-lang-chevron", null, classes, { "accessify-lang-chevron--open": get(langDropdownOpen) });
6821
7049
  div_1.dir = div_1.dir;
6822
7050
  },
6823
7051
  [
@@ -6825,9 +7053,13 @@ function WidgetApp($$anchor, $$props) {
6825
7053
  () => isRtlLang(get(widgetLang)) ? "rtl" : "ltr"
6826
7054
  ]
6827
7055
  );
7056
+ delegated("click", button, () => {
7057
+ set(langDropdownOpen, !get(langDropdownOpen));
7058
+ });
6828
7059
  append($$anchor, fragment);
6829
7060
  return pop($$exports);
6830
7061
  }
7062
+ delegate(["click"]);
6831
7063
  const CHECKMARK_SVG = encodeURIComponent(
6832
7064
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>'
6833
7065
  );
@@ -6836,652 +7068,1105 @@ function createWidgetStyles(config2) {
6836
7068
  const isRight = pos.includes("right");
6837
7069
  const isBottom = pos.includes("bottom");
6838
7070
  const z = config2.zIndex || 999999;
7071
+ const panelRadius = isRight ? "20px 0 0 20px" : "0 20px 20px 0";
7072
+ const hiddenTransform = isRight ? "translateX(24px)" : "translateX(-24px)";
7073
+ const c = config2;
7074
+ const brandOverrides = [
7075
+ c.brandPrimaryColor ? `--surface: ${c.brandPrimaryColor};` : "",
7076
+ c.brandSurfaceColor ? `--surface-dim: ${c.brandSurfaceColor};` : "",
7077
+ c.brandCardColor ? `--surface-card: ${c.brandCardColor};` : "",
7078
+ c.brandHoverColor ? `--surface-hover: ${c.brandHoverColor};` : "",
7079
+ c.brandTextColor ? `--text: ${c.brandTextColor};` : "",
7080
+ c.brandTextDimColor ? `--text-dim: ${c.brandTextDimColor};` : "",
7081
+ c.brandAccentColor ? `--accent: ${c.brandAccentColor}; --accent-soft: ${c.brandAccentColor}26;` : "",
7082
+ c.brandBorderColor ? `--border: ${c.brandBorderColor}; --border-hl: ${c.brandBorderColor};` : ""
7083
+ ].filter(Boolean).join("\n ");
7084
+ const triggerBg = c.brandTriggerColor || "";
7085
+ const triggerIcon = c.brandTriggerIconColor || "";
7086
+ const panelW = c.panelWidth ? `${c.panelWidth}px` : "420px";
6839
7087
  return `
6840
7088
  :host {
6841
7089
  all: initial;
6842
- font-family: ui-rounded, "SF Pro Rounded", "Avenir Next", "Segoe UI", sans-serif;
7090
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
6843
7091
  font-size: 15px;
6844
7092
  line-height: 1.5;
6845
- color-scheme: light dark;
7093
+ color-scheme: dark;
6846
7094
  }
6847
7095
 
6848
- *, *::before, *::after {
6849
- box-sizing: border-box;
6850
- margin: 0;
6851
- padding: 0;
6852
- }
7096
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
6853
7097
 
6854
7098
  #accessify-mount {
6855
- --space-1: 4px;
6856
- --space-2: 8px;
6857
- --space-3: 12px;
6858
- --space-4: 16px;
6859
- --space-5: 20px;
6860
- --space-6: 24px;
6861
- --space-7: 32px;
6862
- --radius-sm: 10px;
6863
- --radius-md: 16px;
6864
- --radius-lg: 24px;
6865
- --radius-pill: 999px;
6866
- --text-xs: 11px;
6867
- --text-sm: 13px;
6868
- --text-base: 15px;
6869
- --text-lg: 18px;
6870
- --text-xl: 24px;
6871
- --font-medium: 500;
6872
- --font-semibold: 600;
6873
- --font-bold: 700;
6874
- --surface: #f6efe4;
6875
- --surface-strong: #fffdf9;
6876
- --surface-muted: #ece4d8;
6877
- --surface-tint: #e2eee8;
6878
- --text: #17212b;
6879
- --text-secondary: #52616f;
6880
- --border: rgba(23, 33, 43, 0.12);
6881
- --border-strong: rgba(23, 33, 43, 0.22);
6882
- --accent: #0f766e;
6883
- --accent-strong: #0b5c56;
6884
- --accent-soft: rgba(15, 118, 110, 0.12);
6885
- --signal: #c97a1e;
6886
- --signal-soft: rgba(201, 122, 30, 0.14);
6887
- --text-on-accent: #ffffff;
6888
- --shadow-sm: 0 12px 24px rgba(16, 24, 32, 0.08);
6889
- --shadow-lg: 0 28px 80px rgba(9, 21, 30, 0.2);
6890
- --transition-fast: 140ms ease;
6891
- --transition-normal: 220ms ease;
6892
- }
6893
-
6894
- @media (prefers-color-scheme: dark) {
6895
- #accessify-mount:not([data-theme="light"]) {
6896
- --surface: #0f1a22;
6897
- --surface-strong: #132330;
6898
- --surface-muted: #1a2b38;
6899
- --surface-tint: #17323a;
6900
- --text: #f4f7fa;
6901
- --text-secondary: #b4c3ce;
6902
- --border: rgba(244, 247, 250, 0.12);
6903
- --border-strong: rgba(244, 247, 250, 0.22);
6904
- --accent: #2dd4bf;
6905
- --accent-strong: #16b5a1;
6906
- --accent-soft: rgba(45, 212, 191, 0.12);
6907
- --signal: #f6ad55;
6908
- --signal-soft: rgba(246, 173, 85, 0.16);
6909
- --shadow-sm: 0 12px 24px rgba(0, 0, 0, 0.28);
6910
- --shadow-lg: 0 28px 80px rgba(0, 0, 0, 0.45);
6911
- }
6912
- }
6913
-
6914
- #accessify-mount[data-theme="dark"] {
6915
- --surface: #0f1a22;
6916
- --surface-strong: #132330;
6917
- --surface-muted: #1a2b38;
6918
- --surface-tint: #17323a;
6919
- --text: #f4f7fa;
6920
- --text-secondary: #b4c3ce;
6921
- --border: rgba(244, 247, 250, 0.12);
6922
- --border-strong: rgba(244, 247, 250, 0.22);
6923
- --accent: #2dd4bf;
6924
- --accent-strong: #16b5a1;
6925
- --accent-soft: rgba(45, 212, 191, 0.12);
6926
- --signal: #f6ad55;
6927
- --signal-soft: rgba(246, 173, 85, 0.16);
6928
- --shadow-sm: 0 12px 24px rgba(0, 0, 0, 0.28);
6929
- --shadow-lg: 0 28px 80px rgba(0, 0, 0, 0.45);
7099
+ --gap: 14px;
7100
+ --pad: 18px;
7101
+ --radius: 12px;
7102
+ --radius-lg: 16px;
7103
+ --pill: 999px;
7104
+ --surface: #3d4775;
7105
+ --surface-card: #4a5387;
7106
+ --surface-dim: #353e6a;
7107
+ --surface-hover: #545d8f;
7108
+ --text: #f0f0f5;
7109
+ --text-dim: #b8bdd4;
7110
+ --border: rgba(255,255,255,0.10);
7111
+ --border-hl: rgba(255,255,255,0.20);
7112
+ --accent: #e8837c;
7113
+ --accent-soft: rgba(232,131,124,0.15);
7114
+ --accent-on: #fff;
7115
+ --shadow: 0 12px 48px rgba(0,0,0,0.35);
7116
+ --fast: 140ms ease;
7117
+ ${brandOverrides}
6930
7118
  }
6931
7119
 
6932
- .sr-only {
6933
- position: absolute;
6934
- width: 1px;
6935
- height: 1px;
6936
- padding: 0;
6937
- margin: -1px;
6938
- overflow: hidden;
6939
- clip: rect(0, 0, 0, 0);
6940
- white-space: nowrap;
6941
- border-width: 0;
6942
- }
6943
-
6944
- *:focus-visible {
6945
- outline: 3px solid var(--signal);
6946
- outline-offset: 3px;
6947
- border-radius: 12px;
6948
- }
7120
+ .sr-only { position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0; }
7121
+ *:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
6949
7122
 
7123
+ /* ─── Trigger ─── */
6950
7124
  .accessify-trigger {
6951
7125
  position: fixed;
6952
7126
  ${isBottom ? "bottom: 20px" : "top: 20px"};
6953
7127
  ${isRight ? "right: 20px" : "left: 20px"};
6954
7128
  z-index: ${z};
6955
- width: 64px;
6956
- height: 64px;
6957
- border: none;
6958
- border-radius: 22px;
6959
- background:
6960
- radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.22), transparent 42%),
6961
- linear-gradient(145deg, #0f766e 0%, #144f67 55%, #c97a1e 100%);
6962
- color: #ffffff;
7129
+ width: 56px; height: 56px;
7130
+ border: none; border-radius: 16px;
7131
+ background: ${triggerBg || "linear-gradient(135deg, #545d8f, #3d4775)"};
7132
+ color: ${triggerIcon || "#e8837c"};
6963
7133
  cursor: pointer;
6964
- display: flex;
6965
- align-items: center;
6966
- justify-content: center;
6967
- box-shadow: 0 18px 42px rgba(12, 36, 46, 0.34);
6968
- transition: transform var(--transition-fast), box-shadow var(--transition-fast), filter var(--transition-fast);
6969
- }
6970
-
6971
- .accessify-trigger:hover {
6972
- transform: translateY(-2px) scale(1.02);
6973
- box-shadow: 0 22px 48px rgba(12, 36, 46, 0.42);
6974
- filter: saturate(1.06);
6975
- }
6976
-
6977
- .accessify-trigger:active {
6978
- transform: translateY(0) scale(0.98);
6979
- }
6980
-
6981
- .accessify-trigger svg {
6982
- width: 30px;
6983
- height: 30px;
6984
- }
7134
+ display: flex; align-items: center; justify-content: center;
7135
+ box-shadow: 0 6px 20px rgba(50,60,100,0.5);
7136
+ transition: transform var(--fast), box-shadow var(--fast), opacity var(--fast);
7137
+ touch-action: none;
7138
+ }
7139
+ .accessify-trigger:hover { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(50,60,100,0.6); }
7140
+ .accessify-trigger:active { transform: scale(0.96); }
7141
+ .accessify-trigger[aria-expanded="true"] { opacity:0; pointer-events:none; transform:scale(0.9); }
7142
+ .accessify-trigger svg { width: 26px; height: 26px; }
7143
+ .accessify-trigger--dragging { cursor: grabbing !important; opacity: 0.85; }
6985
7144
 
7145
+ /* ─── Panel ─── */
6986
7146
  .accessify-panel {
6987
- position: fixed;
6988
- ${isBottom ? "bottom: 96px" : "top: 96px"};
6989
- ${isRight ? "right: 20px" : "left: 20px"};
7147
+ position: fixed; top: 0; bottom: 0;
7148
+ ${isRight ? "right: 0" : "left: 0"};
6990
7149
  z-index: ${z - 1};
6991
- width: 476px;
6992
- max-height: calc(100vh - 120px);
6993
- display: flex;
6994
- flex-direction: column;
6995
- overflow: hidden;
6996
- border-radius: 28px;
6997
- border: 1px solid var(--border);
6998
- background:
6999
- radial-gradient(circle at top right, rgba(201, 122, 30, 0.16), transparent 36%),
7000
- linear-gradient(180deg, var(--surface-strong) 0%, var(--surface) 100%);
7150
+ width: min(${panelW}, 100vw); height: 100vh;
7151
+ display: flex; flex-direction: column;
7152
+ border-radius: ${panelRadius};
7153
+ background: var(--surface);
7001
7154
  color: var(--text);
7002
- box-shadow: var(--shadow-lg);
7003
- transform-origin: ${isBottom ? "bottom" : "top"} ${isRight ? "right" : "left"};
7004
- transition: opacity var(--transition-normal), transform var(--transition-normal);
7005
- backdrop-filter: blur(18px);
7006
- }
7007
-
7008
- .accessify-panel[aria-hidden="true"] {
7009
- opacity: 0;
7010
- transform: scale(0.96) translateY(${isBottom ? "12px" : "-12px"});
7011
- pointer-events: none;
7012
- display: none;
7013
- }
7014
-
7015
- .accessify-panel[aria-hidden="false"] {
7016
- opacity: 1;
7017
- transform: scale(1) translateY(0);
7155
+ box-shadow: var(--shadow);
7156
+ transition: opacity 200ms ease, transform 200ms ease;
7018
7157
  }
7158
+ .accessify-panel[aria-hidden="true"] { opacity:0; transform:${hiddenTransform}; pointer-events:none; display:none; }
7159
+ .accessify-panel[aria-hidden="false"] { opacity:1; transform:translateX(0); }
7019
7160
 
7161
+ /* ─── Header ─── */
7020
7162
  .accessify-header {
7021
- position: relative;
7022
- display: grid;
7023
- gap: var(--space-4);
7024
- padding: var(--space-5);
7025
- background:
7026
- radial-gradient(circle at top left, rgba(255, 255, 255, 0.12), transparent 34%),
7027
- linear-gradient(145deg, #0f2430 0%, #103e4c 52%, #1a5d4b 100%);
7028
- color: #f5fbff;
7029
- }
7030
-
7031
- .accessify-header-copy {
7032
- display: grid;
7033
- gap: var(--space-2);
7034
- }
7035
-
7036
- .accessify-header-kicker {
7037
- display: inline-flex;
7038
- align-items: center;
7039
- width: fit-content;
7040
- padding: 6px 10px;
7041
- border-radius: var(--radius-pill);
7042
- background: rgba(255, 255, 255, 0.12);
7043
- color: #ffe2b8;
7044
- font-size: var(--text-xs);
7045
- font-weight: var(--font-semibold);
7046
- letter-spacing: 0.06em;
7047
- text-transform: uppercase;
7048
- }
7049
-
7050
- .accessify-header-title {
7051
- display: grid;
7052
- gap: 6px;
7053
- }
7054
-
7055
- .accessify-header h2 {
7056
- font-size: var(--text-xl);
7057
- font-weight: var(--font-bold);
7058
- line-height: 1.1;
7059
- }
7060
-
7061
- .accessify-header p {
7062
- color: rgba(245, 251, 255, 0.82);
7063
- font-size: var(--text-sm);
7064
- max-width: 34ch;
7065
- }
7066
-
7067
- .accessify-header-toolbar {
7068
- display: flex;
7069
- align-items: center;
7070
- gap: var(--space-3);
7071
- justify-content: space-between;
7072
- flex-wrap: wrap;
7073
- }
7074
-
7075
- .accessify-language-picker {
7076
- position: relative;
7077
- display: inline-flex;
7078
- align-items: center;
7079
- gap: 10px;
7080
- min-width: 172px;
7081
- padding: 0 14px;
7082
- border-radius: 16px;
7083
- border: 1px solid rgba(255, 255, 255, 0.16);
7084
- background: rgba(10, 23, 29, 0.24);
7085
- color: #ffffff;
7086
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
7087
- }
7088
-
7089
- .accessify-language-icon {
7090
- display: inline-flex;
7091
- align-items: center;
7092
- justify-content: center;
7093
- color: #9ce4da;
7094
- }
7095
-
7096
- .accessify-language-icon svg {
7097
- width: 16px;
7098
- height: 16px;
7099
- }
7100
-
7101
- .accessify-language-select {
7102
- appearance: none;
7103
- width: 100%;
7104
- border: none;
7105
- background: transparent;
7106
- color: #ffffff;
7107
- padding: 12px 0;
7108
- font-size: var(--text-sm);
7109
- font-weight: var(--font-medium);
7110
- cursor: pointer;
7111
- }
7112
-
7113
- .accessify-language-select option {
7114
- color: #17212b;
7163
+ display: flex; align-items: center; justify-content: space-between;
7164
+ padding: 14px var(--pad);
7165
+ border-bottom: 1px solid var(--border);
7166
+ background: var(--surface-dim);
7167
+ flex-shrink: 0;
7115
7168
  }
7169
+ .accessify-header-left { display: flex; align-items: center; gap: 10px; }
7170
+ .accessify-header-actions { display: flex; align-items: center; gap: 6px; }
7116
7171
 
7117
- .accessify-header-actions {
7118
- display: flex;
7119
- align-items: center;
7120
- gap: 10px;
7121
- flex-wrap: wrap;
7122
- }
7172
+ .accessify-logo { display: inline-flex; color: var(--accent); }
7173
+ .accessify-logo svg { height: 24px; width: auto; }
7123
7174
 
7124
7175
  .accessify-header-btn {
7125
- display: inline-flex;
7126
- align-items: center;
7127
- justify-content: center;
7128
- gap: 8px;
7129
- padding: 11px 14px;
7130
- border: 1px solid rgba(255, 255, 255, 0.16);
7131
- border-radius: 16px;
7132
- background: rgba(10, 23, 29, 0.24);
7133
- color: #f5fbff;
7134
- cursor: pointer;
7135
- font-size: var(--text-sm);
7136
- font-weight: var(--font-medium);
7137
- transition: background var(--transition-fast), transform var(--transition-fast), border-color var(--transition-fast);
7138
- }
7139
-
7140
- .accessify-header-btn:hover {
7141
- background: rgba(255, 255, 255, 0.12);
7142
- border-color: rgba(255, 255, 255, 0.24);
7143
- transform: translateY(-1px);
7144
- }
7145
-
7146
- .accessify-header-btn svg {
7147
- width: 16px;
7148
- height: 16px;
7149
- }
7150
-
7151
- .accessify-header-btn--icon {
7152
- width: 44px;
7153
- padding-inline: 0;
7154
- }
7155
-
7156
- .accessify-status {
7157
- display: flex;
7158
- align-items: center;
7159
- justify-content: space-between;
7160
- gap: var(--space-3);
7161
- padding: 14px var(--space-5);
7162
- border-bottom: 1px solid var(--border);
7163
- background: linear-gradient(180deg, rgba(15, 118, 110, 0.06), transparent);
7164
- }
7165
-
7166
- .accessify-status-count {
7167
- display: inline-flex;
7168
- align-items: center;
7169
- gap: 8px;
7170
- min-height: 36px;
7171
- padding: 0 14px;
7172
- border-radius: var(--radius-pill);
7173
- background: var(--accent-soft);
7174
- color: var(--accent-strong);
7175
- font-size: var(--text-sm);
7176
- font-weight: var(--font-semibold);
7177
- }
7176
+ display: inline-flex; align-items: center; justify-content: center;
7177
+ width: 34px; height: 34px;
7178
+ border: 1px solid var(--border); border-radius: 10px;
7179
+ background: var(--surface); color: var(--text-dim);
7180
+ cursor: pointer; transition: background var(--fast), color var(--fast);
7181
+ }
7182
+ .accessify-header-btn:hover { background: var(--surface-hover); color: var(--text); }
7183
+ .accessify-header-btn svg { width: 16px; height: 16px; }
7184
+ .accessify-header-btn--icon { padding: 0; }
7185
+ .accessify-header-btn--close { background: transparent; border-color: transparent; }
7178
7186
 
7179
- .accessify-status-count[data-count="0"] {
7180
- background: var(--surface-muted);
7181
- color: var(--text-secondary);
7187
+ /* ─── Shared Accordion (Language + Profiles) ─── */
7188
+ .accessify-lang-dropdown,
7189
+ .accessify-accordion {
7190
+ border-radius: var(--radius);
7191
+ border: 1px solid var(--border);
7192
+ overflow: clip;
7182
7193
  }
7183
7194
 
7184
- .accessify-status-reset {
7185
- border: none;
7186
- background: none;
7187
- color: var(--text-secondary);
7188
- cursor: pointer;
7189
- font-size: var(--text-sm);
7190
- font-weight: var(--font-medium);
7191
- padding: 8px 0;
7192
- transition: color var(--transition-fast);
7193
- }
7195
+ .accessify-lang-toggle,
7196
+ .accessify-accordion-toggle {
7197
+ display: flex; align-items: center; justify-content: space-between;
7198
+ width: 100%; padding: 14px 14px;
7199
+ border: none; background: var(--surface-card);
7200
+ color: var(--text); cursor: pointer; font-family: inherit;
7201
+ font-size: 15px; font-weight: 700;
7202
+ transition: background var(--fast);
7203
+ }
7204
+ .accessify-lang-toggle:hover,
7205
+ .accessify-accordion-toggle:hover { background: var(--surface-hover); }
7194
7206
 
7195
- .accessify-status-reset:hover {
7196
- color: var(--signal);
7207
+ .accessify-lang-toggle-left,
7208
+ .accessify-accordion-toggle-left {
7209
+ display: flex; align-items: center; gap: 10px;
7197
7210
  }
7211
+ .accessify-lang-toggle-left svg,
7212
+ .accessify-accordion-toggle-left svg { width: 20px; height: 20px; color: var(--accent); }
7198
7213
 
7199
- .accessify-body {
7200
- display: grid;
7201
- gap: var(--space-4);
7202
- padding: var(--space-4);
7203
- overflow-y: auto;
7214
+ .accessify-lang-chevron,
7215
+ .accessify-accordion-chevron {
7216
+ width: 18px; height: 18px; color: var(--text-dim);
7217
+ transition: transform 200ms ease; flex-shrink: 0;
7204
7218
  }
7219
+ .accessify-lang-chevron--open,
7220
+ .accessify-accordion-chevron--open { transform: rotate(180deg); }
7205
7221
 
7206
- .accessify-section {
7207
- display: grid;
7208
- gap: var(--space-4);
7209
- padding: var(--space-4);
7210
- border-radius: var(--radius-lg);
7211
- border: 1px solid var(--border);
7212
- background: rgba(255, 255, 255, 0.32);
7213
- box-shadow: var(--shadow-sm);
7222
+ /* Shared list inside accordion */
7223
+ .accessify-lang-list,
7224
+ .accessify-accordion-list {
7225
+ display: flex; flex-direction: column;
7226
+ max-height: 280px; overflow-y: auto;
7227
+ background: var(--surface-dim);
7228
+ border-top: 1px solid var(--border);
7214
7229
  }
7230
+ .accessify-lang-list::-webkit-scrollbar,
7231
+ .accessify-accordion-list::-webkit-scrollbar { width: 4px; }
7232
+ .accessify-lang-list::-webkit-scrollbar-thumb,
7233
+ .accessify-accordion-list::-webkit-scrollbar-thumb { background: var(--border-hl); border-radius: 2px; }
7215
7234
 
7216
- .accessify-section--spotlight {
7217
- background:
7218
- linear-gradient(180deg, rgba(15, 118, 110, 0.08) 0%, rgba(255, 255, 255, 0.28) 100%);
7219
- border-color: color-mix(in srgb, var(--accent) 24%, var(--border));
7220
- }
7235
+ /* Language options */
7236
+ .accessify-lang-option {
7237
+ display: flex; align-items: center; justify-content: space-between;
7238
+ padding: 12px var(--pad);
7239
+ border: none; background: transparent;
7240
+ color: var(--text); cursor: pointer;
7241
+ font-size: 14px; font-weight: 500; font-family: inherit;
7242
+ transition: background var(--fast);
7243
+ }
7244
+ .accessify-lang-option:hover { background: var(--surface-hover); }
7245
+ .accessify-lang-option[data-active="true"] { color: var(--accent); font-weight: 700; }
7246
+ .accessify-lang-option svg { width: 18px; height: 18px; color: var(--accent); flex-shrink: 0; }
7221
7247
 
7222
- .accessify-section-head {
7223
- display: flex;
7224
- align-items: flex-start;
7225
- justify-content: space-between;
7226
- gap: var(--space-3);
7227
- }
7248
+ /* Profile options (with icon + description) */
7249
+ .accessify-accordion-option {
7250
+ display: flex; align-items: center; justify-content: space-between;
7251
+ padding: 14px var(--pad);
7252
+ border: none; background: transparent;
7253
+ color: var(--text); cursor: pointer;
7254
+ font-family: inherit; text-align: left;
7255
+ transition: background var(--fast);
7256
+ }
7257
+ .accessify-accordion-option:hover { background: var(--surface-hover); }
7258
+ .accessify-accordion-option[data-active="true"] { background: var(--accent-soft); }
7228
7259
 
7229
- .accessify-section-head h3 {
7230
- font-size: var(--text-lg);
7231
- font-weight: var(--font-semibold);
7232
- line-height: 1.15;
7233
- color: var(--text);
7260
+ .accessify-accordion-option-left {
7261
+ display: flex; align-items: center; gap: 12px; min-width: 0;
7234
7262
  }
7235
-
7236
- .accessify-section-head p {
7237
- margin-top: 6px;
7238
- color: var(--text-secondary);
7239
- font-size: var(--text-sm);
7240
- max-width: 46ch;
7263
+ .accessify-accordion-option-icon {
7264
+ display: inline-flex; align-items: center; justify-content: center;
7265
+ width: 40px; height: 40px; border-radius: 12px;
7266
+ background: var(--surface-card); color: var(--accent); flex-shrink: 0;
7241
7267
  }
7242
-
7243
- .accessify-section-kicker {
7244
- display: inline-flex;
7245
- align-items: center;
7246
- margin-bottom: 8px;
7247
- color: var(--signal);
7248
- font-size: var(--text-xs);
7249
- font-weight: var(--font-semibold);
7250
- letter-spacing: 0.06em;
7251
- text-transform: uppercase;
7268
+ .accessify-accordion-option-icon svg { width: 24px; height: 24px; stroke-width: 2.5; }
7269
+ .accessify-accordion-option-text {
7270
+ display: flex; flex-direction: column; gap: 2px; min-width: 0;
7252
7271
  }
7253
-
7254
- .accessify-control-grid {
7255
- display: grid;
7256
- grid-template-columns: repeat(2, minmax(0, 1fr));
7257
- gap: var(--space-3);
7272
+ .accessify-accordion-option-name {
7273
+ font-size: 14px; font-weight: 700; line-height: 1.3;
7258
7274
  }
7259
-
7260
- .accessify-control-card {
7261
- display: grid;
7262
- gap: var(--space-3);
7263
- padding: var(--space-4);
7264
- border-radius: 22px;
7265
- border: 1px solid var(--border);
7266
- background: var(--surface-strong);
7275
+ .accessify-accordion-option-desc {
7276
+ font-size: 12px; font-weight: 400; color: var(--text-dim); line-height: 1.3;
7267
7277
  }
7268
-
7269
- .accessify-control-copy {
7270
- display: grid;
7271
- grid-template-columns: auto 1fr;
7272
- gap: var(--space-3);
7273
- align-items: start;
7278
+ .accessify-accordion-option > svg {
7279
+ width: 20px; height: 20px; color: var(--accent); flex-shrink: 0; margin-left: 8px;
7274
7280
  }
7275
7281
 
7276
- .accessify-control-copy h4 {
7277
- font-size: var(--text-base);
7278
- font-weight: var(--font-semibold);
7279
- color: var(--text);
7282
+ /* ─── Scrollable Body ─── */
7283
+ .accessify-body {
7284
+ flex: 1 1 auto;
7285
+ display: flex; flex-direction: column; gap: var(--gap);
7286
+ padding: var(--gap) var(--pad);
7287
+ overflow-y: auto; overscroll-behavior: contain;
7280
7288
  }
7281
7289
 
7282
- .accessify-control-copy p {
7283
- margin-top: 4px;
7284
- color: var(--text-secondary);
7285
- font-size: var(--text-sm);
7286
- }
7290
+ /* ─── Contrast Row ─── */
7291
+ .accessify-control-row {
7292
+ display: flex; flex-direction: column; gap: 10px;
7293
+ padding: 14px 16px;
7294
+ border-radius: var(--radius); border: 1px solid var(--border);
7295
+ background: var(--surface-card);
7296
+ }
7297
+ .accessify-control-label {
7298
+ display: flex; align-items: center; gap: 8px;
7299
+ }
7300
+ .accessify-control-label-icon {
7301
+ display: inline-flex; align-items: center; justify-content: center;
7302
+ width: 34px; height: 34px; border-radius: 10px;
7303
+ background: var(--surface-dim); color: var(--accent); flex-shrink: 0;
7304
+ }
7305
+ .accessify-control-label-icon svg { width: 18px; height: 18px; }
7306
+ .accessify-control-label-text { font-size: 14px; font-weight: 600; }
7287
7307
 
7288
- .accessify-chip-group {
7289
- display: flex;
7290
- flex-wrap: wrap;
7291
- gap: 8px;
7308
+ /* Contrast chips */
7309
+ .accessify-chip-row {
7310
+ display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
7292
7311
  }
7293
-
7294
7312
  .accessify-chip {
7295
- min-height: 40px;
7296
- padding: 0 14px;
7297
- border-radius: var(--radius-pill);
7298
- border: 1px solid var(--border);
7299
- background: var(--surface);
7300
- color: var(--text);
7301
- cursor: pointer;
7302
- font-size: var(--text-sm);
7303
- font-weight: var(--font-medium);
7304
- transition: transform var(--transition-fast), background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
7305
- }
7313
+ min-height: 48px; padding: 6px 8px;
7314
+ border-radius: 10px; border: 1px solid var(--border);
7315
+ background: var(--surface-dim); color: var(--text);
7316
+ cursor: pointer; font-size: 11px; font-weight: 600; font-family: inherit;
7317
+ display: inline-flex; align-items: center; justify-content: center;
7318
+ text-align: center; line-height: 1.25;
7319
+ transition: background var(--fast), color var(--fast), border-color var(--fast);
7320
+ }
7321
+ .accessify-chip:hover { border-color: var(--border-hl); background: var(--surface-hover); }
7322
+ .accessify-chip[data-active="true"] { background: var(--accent); color: var(--accent-on); border-color: var(--accent); }
7323
+ .accessify-chip--icon { gap: 6px; }
7324
+ .accessify-chip-icon { display: inline-flex; width: 14px; height: 14px; flex-shrink: 0; }
7325
+ .accessify-chip-icon svg { width: 14px; height: 14px; }
7306
7326
 
7307
- .accessify-chip:hover {
7308
- transform: translateY(-1px);
7309
- border-color: var(--border-strong);
7310
- background: var(--surface-strong);
7311
- }
7312
-
7313
- .accessify-chip[data-active="true"] {
7314
- background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%);
7315
- color: var(--text-on-accent);
7316
- border-color: transparent;
7317
- box-shadow: 0 12px 22px rgba(15, 118, 110, 0.24);
7318
- }
7319
-
7320
- .accessify-chip-group--sizes .accessify-chip {
7321
- min-width: 68px;
7322
- justify-content: center;
7327
+ /* Text Size stepper */
7328
+ .accessify-control-row:has(.accessify-stepper) {
7329
+ flex-direction: row; align-items: center; justify-content: space-between; gap: var(--gap);
7330
+ }
7331
+ .accessify-stepper {
7332
+ display: inline-flex; align-items: center;
7333
+ border-radius: 10px; border: 1px solid var(--border);
7334
+ background: var(--surface-dim); height: 36px; overflow: hidden;
7335
+ }
7336
+ .accessify-stepper-btn {
7337
+ display: inline-flex; align-items: center; justify-content: center;
7338
+ width: 36px; height: 100%; border: none; background: transparent;
7339
+ color: var(--text); cursor: pointer; font-size: 18px; font-weight: 700;
7340
+ transition: background var(--fast); font-family: inherit;
7341
+ }
7342
+ .accessify-stepper-btn:hover:not(:disabled) { background: var(--surface-hover); }
7343
+ .accessify-stepper-btn:disabled { opacity: 0.3; cursor: default; }
7344
+ .accessify-stepper-value {
7345
+ min-width: 48px; text-align: center;
7346
+ font-size: 13px; font-weight: 600; color: var(--text); user-select: none;
7323
7347
  }
7324
7348
 
7349
+ /* ─── Feature Cards (2-column grid) ─── */
7325
7350
  .accessify-features {
7326
- display: grid;
7327
- grid-template-columns: repeat(2, minmax(0, 1fr));
7328
- gap: var(--space-3);
7351
+ display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap);
7329
7352
  }
7330
-
7331
- .accessify-card {
7353
+ .accessify-card-wrap {
7332
7354
  position: relative;
7333
- display: grid;
7334
- gap: 10px;
7335
- align-content: start;
7336
- min-height: 156px;
7337
- padding: var(--space-4);
7338
- border-radius: 22px;
7339
- border: 1px solid var(--border);
7340
- background: color-mix(in srgb, var(--surface-strong) 88%, transparent);
7341
- color: var(--text);
7342
- cursor: pointer;
7343
- text-align: left;
7344
- font-family: inherit;
7345
- font-size: inherit;
7346
- transition: transform var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast), box-shadow var(--transition-fast);
7347
- }
7348
-
7349
- .accessify-card:hover {
7350
- transform: translateY(-2px);
7351
- border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
7352
- box-shadow: var(--shadow-sm);
7355
+ overflow: visible;
7353
7356
  }
7354
-
7355
- .accessify-card[data-active="true"] {
7356
- border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
7357
- background:
7358
- linear-gradient(180deg, rgba(15, 118, 110, 0.08) 0%, rgba(255, 255, 255, 0.18) 100%);
7359
- box-shadow: 0 18px 32px rgba(15, 118, 110, 0.16);
7360
- }
7361
-
7357
+ .accessify-card {
7358
+ position: relative; width: 100%;
7359
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
7360
+ gap: 8px; padding: 18px 10px; min-height: 105px;
7361
+ border-radius: var(--radius-lg); border: 1px solid var(--border);
7362
+ background: var(--surface-card); color: var(--text);
7363
+ cursor: pointer; text-align: center; font-family: inherit;
7364
+ transition: transform var(--fast), border-color var(--fast), background var(--fast), box-shadow var(--fast);
7365
+ }
7366
+ .accessify-card:hover { transform: translateY(-2px); border-color: var(--border-hl); box-shadow: 0 4px 16px rgba(0,0,0,0.15); }
7367
+ .accessify-card[data-active="true"] { border-color: var(--accent); background: var(--accent-soft); }
7362
7368
  .accessify-card[data-active="true"]::after {
7363
- content: '';
7364
- position: absolute;
7365
- top: 14px;
7366
- right: 14px;
7367
- width: 20px;
7368
- height: 20px;
7369
- border-radius: 999px;
7370
- background: var(--accent);
7369
+ content: ''; position: absolute; top: 8px; ${isRight ? "right: 8px" : "left: 8px"};
7370
+ width: 18px; height: 18px; border-radius: 999px; background: var(--accent);
7371
7371
  mask-image: url("data:image/svg+xml,${CHECKMARK_SVG}");
7372
- mask-size: 12px;
7373
- mask-repeat: no-repeat;
7374
- mask-position: center;
7372
+ mask-size: 10px; mask-repeat: no-repeat; mask-position: center;
7375
7373
  -webkit-mask-image: url("data:image/svg+xml,${CHECKMARK_SVG}");
7376
- -webkit-mask-size: 12px;
7377
- -webkit-mask-repeat: no-repeat;
7378
- -webkit-mask-position: center;
7379
- }
7380
-
7381
- .accessify-card-top {
7382
- display: flex;
7383
- align-items: center;
7384
- justify-content: space-between;
7385
- gap: var(--space-2);
7374
+ -webkit-mask-size: 10px; -webkit-mask-repeat: no-repeat; -webkit-mask-position: center;
7386
7375
  }
7387
-
7388
7376
  .accessify-card-icon {
7389
- display: inline-flex;
7390
- align-items: center;
7391
- justify-content: center;
7392
- width: 44px;
7393
- height: 44px;
7394
- border-radius: 16px;
7395
- background: var(--surface-tint);
7396
- color: var(--accent-strong);
7397
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
7377
+ display: inline-flex; align-items: center; justify-content: center;
7378
+ width: 48px; height: 48px; border-radius: 14px;
7379
+ background: var(--surface-dim); color: var(--accent);
7398
7380
  }
7381
+ .accessify-card-icon svg { width: 28px; height: 28px; stroke-width: 2.5; }
7382
+ .accessify-card-label { font-size: 12px; font-weight: 600; line-height: 1.25; }
7399
7383
 
7400
- .accessify-card-icon svg {
7401
- width: 20px;
7402
- height: 20px;
7403
- }
7404
-
7405
- .accessify-card-label {
7406
- font-size: var(--text-base);
7407
- font-weight: var(--font-semibold);
7408
- line-height: 1.2;
7409
- color: var(--text);
7384
+ /* Info icon + tooltip */
7385
+ .accessify-card-info {
7386
+ position: absolute; top: 6px; ${isRight ? "right: 6px" : "left: 6px"}; z-index: 10;
7387
+ display: inline-flex; align-items: center; justify-content: center;
7388
+ width: 22px; height: 22px; border-radius: 999px;
7389
+ color: var(--text-dim); cursor: help; background: none; border: none;
7390
+ opacity: 0.35; transition: opacity var(--fast), color var(--fast);
7391
+ }
7392
+ .accessify-card-info svg { width: 14px; height: 14px; }
7393
+ .accessify-card-info:hover, .accessify-card-info:focus-visible { opacity: 1; color: var(--accent); }
7394
+ .accessify-card-tooltip {
7395
+ display: none; position: absolute;
7396
+ top: calc(100% + 6px); ${isRight ? "right: 0" : "left: 0"};
7397
+ width: 180px; padding: 10px 12px;
7398
+ border-radius: 10px; background: #2a3158; color: var(--text);
7399
+ font-size: 12px; font-weight: 500; line-height: 1.4;
7400
+ text-align: left; white-space: normal;
7401
+ box-shadow: 0 6px 24px rgba(0,0,0,0.5);
7402
+ pointer-events: none; z-index: 100;
7403
+ }
7404
+ .accessify-card-info:hover .accessify-card-tooltip,
7405
+ .accessify-card-info:focus-visible .accessify-card-tooltip {
7406
+ display: block;
7407
+ }
7408
+ .accessify-card-tooltip::before {
7409
+ content: ''; position: absolute; bottom: 100%; ${isRight ? "right: 4px" : "left: 4px"};
7410
+ border: 5px solid transparent; border-bottom-color: #2a3158;
7410
7411
  }
7411
7412
 
7412
- .accessify-card-desc {
7413
- color: var(--text-secondary);
7414
- font-size: var(--text-sm);
7415
- line-height: 1.45;
7413
+ /* ─── Section Divider ─── */
7414
+ .accessify-section-divider {
7415
+ height: 1px;
7416
+ background: var(--border);
7417
+ margin: 4px 0;
7418
+ flex-shrink: 0;
7416
7419
  }
7417
7420
 
7421
+ /* ─── Footer ─── */
7418
7422
  .accessify-footer {
7419
- display: flex;
7420
- align-items: center;
7421
- justify-content: center;
7422
- gap: var(--space-2);
7423
- padding: 14px var(--space-5) var(--space-5);
7423
+ display: flex; align-items: center; justify-content: center; gap: 8px;
7424
+ padding: 8px var(--pad);
7424
7425
  border-top: 1px solid var(--border);
7425
- color: var(--text-secondary);
7426
- font-size: var(--text-xs);
7427
- text-align: center;
7428
- background: linear-gradient(180deg, transparent, rgba(15, 118, 110, 0.04));
7426
+ color: var(--text-dim); font-size: 11px;
7427
+ background: var(--surface-dim); flex-shrink: 0;
7429
7428
  }
7429
+ .accessify-footer a { color: var(--accent); text-decoration: none; font-weight: 600; }
7430
+ .accessify-footer a:hover { text-decoration: underline; }
7430
7431
 
7431
- .accessify-footer a {
7432
- color: var(--accent-strong);
7433
- text-decoration: none;
7434
- font-weight: var(--font-medium);
7432
+ /* ─── Reduced Motion ─── */
7433
+ @media (prefers-reduced-motion: reduce) {
7434
+ *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
7435
7435
  }
7436
7436
 
7437
- .accessify-footer a:hover {
7438
- text-decoration: underline;
7437
+ /* ─── Mobile ─── */
7438
+ @media (max-width: 640px) {
7439
+ .accessify-panel { width: 100vw; border-radius: 0; }
7440
+ .accessify-features { gap: 8px; }
7441
+ .accessify-body { padding: 8px 12px; }
7442
+ .accessify-trigger {
7443
+ ${isBottom ? "bottom: 16px" : "top: 16px"};
7444
+ ${isRight ? "right: 16px" : "left: 16px"};
7445
+ width: 50px; height: 50px; border-radius: 14px;
7446
+ }
7439
7447
  }
7440
-
7441
- @media (prefers-reduced-motion: reduce) {
7442
- *, *::before, *::after {
7443
- animation-duration: 0.01ms !important;
7444
- transition-duration: 0.01ms !important;
7448
+ `;
7449
+ }
7450
+ const IDB_NAME = "accessify-alt-text-cache";
7451
+ const IDB_STORE = "alt-texts";
7452
+ const IDB_REPORT_STORE = "alt-text-reports";
7453
+ const IDB_VERSION = 2;
7454
+ function hashSrc(src) {
7455
+ let hash = 5381;
7456
+ const n = src.toLowerCase().trim();
7457
+ for (let i = 0; i < n.length; i++) {
7458
+ hash = (hash << 5) + hash + n.charCodeAt(i);
7459
+ hash |= 0;
7460
+ }
7461
+ return Math.abs(hash).toString(36);
7462
+ }
7463
+ function openCache() {
7464
+ return new Promise((resolve) => {
7465
+ try {
7466
+ const req = indexedDB.open(IDB_NAME, IDB_VERSION);
7467
+ req.onupgradeneeded = () => {
7468
+ const db = req.result;
7469
+ if (!db.objectStoreNames.contains(IDB_STORE)) db.createObjectStore(IDB_STORE, { keyPath: "key" });
7470
+ if (!db.objectStoreNames.contains(IDB_REPORT_STORE)) db.createObjectStore(IDB_REPORT_STORE, { keyPath: "key" });
7471
+ };
7472
+ req.onsuccess = () => resolve(req.result);
7473
+ req.onerror = () => resolve(null);
7474
+ } catch {
7475
+ resolve(null);
7476
+ }
7477
+ });
7478
+ }
7479
+ async function getAllCachedAltTexts() {
7480
+ const db = await openCache();
7481
+ const map = /* @__PURE__ */ new Map();
7482
+ if (!db) return map;
7483
+ return new Promise((resolve) => {
7484
+ try {
7485
+ const tx = db.transaction(IDB_STORE, "readonly");
7486
+ const req = tx.objectStore(IDB_STORE).getAll();
7487
+ req.onsuccess = () => {
7488
+ const results = req.result;
7489
+ for (const r of results) {
7490
+ map.set(r.src, r.altText);
7491
+ }
7492
+ resolve(map);
7493
+ };
7494
+ req.onerror = () => resolve(map);
7495
+ } catch {
7496
+ resolve(map);
7497
+ }
7498
+ });
7499
+ }
7500
+ async function getCachedAltText(src) {
7501
+ const db = await openCache();
7502
+ if (!db) return null;
7503
+ return new Promise((resolve) => {
7504
+ try {
7505
+ const tx = db.transaction(IDB_STORE, "readonly");
7506
+ const req = tx.objectStore(IDB_STORE).get(hashSrc(src));
7507
+ req.onsuccess = () => resolve(req.result?.altText || null);
7508
+ req.onerror = () => resolve(null);
7509
+ } catch {
7510
+ resolve(null);
7511
+ }
7512
+ });
7513
+ }
7514
+ async function setCachedAltText(src, altText2, langCode) {
7515
+ const db = await openCache();
7516
+ if (!db) return;
7517
+ return new Promise((resolve) => {
7518
+ try {
7519
+ const tx = db.transaction(IDB_STORE, "readwrite");
7520
+ tx.objectStore(IDB_STORE).put({ key: hashSrc(src), src, altText: altText2, lang: langCode, createdAt: Date.now() });
7521
+ tx.oncomplete = () => resolve();
7522
+ tx.onerror = () => resolve();
7523
+ } catch {
7524
+ resolve();
7525
+ }
7526
+ });
7527
+ }
7528
+ async function saveReport(src, altText2, langCode) {
7529
+ const db = await openCache();
7530
+ if (!db) return;
7531
+ return new Promise((resolve) => {
7532
+ try {
7533
+ const tx = db.transaction(IDB_REPORT_STORE, "readwrite");
7534
+ tx.objectStore(IDB_REPORT_STORE).put({
7535
+ key: hashSrc(src) + "_" + Date.now(),
7536
+ src,
7537
+ pageUrl: window.location.href,
7538
+ altText: altText2,
7539
+ lang: langCode,
7540
+ reportedAt: Date.now()
7541
+ });
7542
+ tx.oncomplete = () => resolve();
7543
+ tx.onerror = () => resolve();
7544
+ } catch {
7545
+ resolve();
7546
+ }
7547
+ });
7548
+ }
7549
+ async function removeCachedAltText(src) {
7550
+ const db = await openCache();
7551
+ if (!db) return;
7552
+ return new Promise((resolve) => {
7553
+ try {
7554
+ const tx = db.transaction(IDB_STORE, "readwrite");
7555
+ tx.objectStore(IDB_STORE).delete(hashSrc(src));
7556
+ tx.oncomplete = () => resolve();
7557
+ tx.onerror = () => resolve();
7558
+ } catch {
7559
+ resolve();
7560
+ }
7561
+ });
7562
+ }
7563
+ const DEFAULT_API_BASE = "https://accessify-api.accessify.workers.dev";
7564
+ async function fetchServerAltTexts(siteKey, proxyUrl, lang) {
7565
+ const map = /* @__PURE__ */ new Map();
7566
+ try {
7567
+ const base = proxyUrl || DEFAULT_API_BASE;
7568
+ const pageUrl = window.location.origin + window.location.pathname;
7569
+ const res = await fetch(`${base}/v1/manifest?siteKey=${encodeURIComponent(siteKey)}&url=${encodeURIComponent(pageUrl)}&feature=alt_text&variant=${encodeURIComponent(lang)}`, {
7570
+ headers: { "Accept": "application/json" }
7571
+ });
7572
+ if (!res.ok) return map;
7573
+ const data = await res.json();
7574
+ if (data.blocks) {
7575
+ for (const block2 of data.blocks) {
7576
+ if (block2.selector && block2.result) {
7577
+ map.set(block2.selector, block2.result);
7578
+ }
7445
7579
  }
7446
7580
  }
7447
-
7448
- @media (max-width: 560px) {
7449
- .accessify-panel {
7450
- width: calc(100vw - 24px);
7451
- ${isRight ? "right: 12px" : "left: 12px"};
7452
- ${isBottom ? "bottom: 86px" : "top: 86px"};
7453
- max-height: calc(100vh - 102px);
7581
+ } catch {
7582
+ }
7583
+ return map;
7584
+ }
7585
+ function persistAltTextToServer(siteKey, proxyUrl, imageUrl, altText2, lang) {
7586
+ try {
7587
+ const base = proxyUrl || DEFAULT_API_BASE;
7588
+ const pageUrl = window.location.origin + window.location.pathname;
7589
+ fetch(`${base}/v1/cache/persist-alt-text`, {
7590
+ method: "POST",
7591
+ headers: { "Content-Type": "application/json" },
7592
+ body: JSON.stringify({ siteKey, pageUrl, imageUrl, altText: altText2, lang })
7593
+ }).catch(() => {
7594
+ });
7595
+ } catch {
7596
+ }
7597
+ }
7598
+ let autoApplied = false;
7599
+ async function autoApplyCachedAltTexts() {
7600
+ if (autoApplied) return 0;
7601
+ autoApplied = true;
7602
+ const cache = /* @__PURE__ */ new Map();
7603
+ const widgetConfig = window.Accessify?.config;
7604
+ const siteKey = widgetConfig?.siteKey;
7605
+ const proxyUrl = widgetConfig?.proxyUrl || "";
7606
+ const pageLang = document.documentElement.lang?.split("-")[0] || "en";
7607
+ if (siteKey) {
7608
+ const serverCache = await fetchServerAltTexts(siteKey, proxyUrl, pageLang);
7609
+ for (const [url, alt] of serverCache) {
7610
+ cache.set(url, alt);
7611
+ setCachedAltText(url, alt, pageLang).catch(() => {
7612
+ });
7613
+ }
7614
+ }
7615
+ const localCache = await getAllCachedAltTexts();
7616
+ for (const [url, alt] of localCache) {
7617
+ if (!cache.has(url)) cache.set(url, alt);
7618
+ }
7619
+ if (cache.size === 0) return 0;
7620
+ let applied = 0;
7621
+ const images = document.querySelectorAll("img");
7622
+ images.forEach((img) => {
7623
+ if (img.closest("#accessify-root")) return;
7624
+ const alt = img.getAttribute("alt");
7625
+ if (alt !== null && alt.trim() !== "") return;
7626
+ const cached = cache.get(img.src);
7627
+ if (cached) {
7628
+ img.setAttribute("alt", cached);
7629
+ img.setAttribute("title", cached);
7630
+ applied++;
7631
+ }
7632
+ });
7633
+ const observer = new MutationObserver((mutations) => {
7634
+ for (const m of mutations) {
7635
+ for (const node of m.addedNodes) {
7636
+ const imgs = node instanceof HTMLImageElement ? [node] : node instanceof HTMLElement ? Array.from(node.querySelectorAll("img")) : [];
7637
+ for (const img of imgs) {
7638
+ if (img.closest("#accessify-root")) continue;
7639
+ const a = img.getAttribute("alt");
7640
+ if (a !== null && a.trim() !== "") continue;
7641
+ const c = cache.get(img.src);
7642
+ if (c) {
7643
+ img.setAttribute("alt", c);
7644
+ img.setAttribute("title", c);
7645
+ }
7646
+ }
7454
7647
  }
7455
-
7456
- .accessify-header,
7457
- .accessify-status,
7458
- .accessify-footer {
7459
- padding-inline: var(--space-4);
7648
+ }
7649
+ });
7650
+ observer.observe(document.body, { childList: true, subtree: true });
7651
+ setTimeout(() => observer.disconnect(), 3e4);
7652
+ return applied;
7653
+ }
7654
+ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
7655
+ const siteKey = serverConfig?.siteKey || "";
7656
+ const proxyUrl = serverConfig?.proxyUrl || "";
7657
+ let enabled = false;
7658
+ let styleEl = null;
7659
+ let badgeEl = null;
7660
+ let missingAltImages = [];
7661
+ const processedImages = /* @__PURE__ */ new Map();
7662
+ let autoGenerating = false;
7663
+ let tooltipEl = null;
7664
+ const infoButtons = /* @__PURE__ */ new Map();
7665
+ let reportPanelEl = null;
7666
+ function lang() {
7667
+ return getCurrentWidgetLang() || initialLang;
7668
+ }
7669
+ function isDE() {
7670
+ return lang().startsWith("de");
7671
+ }
7672
+ const STYLE_ID = "accessify-alt-text-styles";
7673
+ const HIGHLIGHT_CLASS = "accessify-alt-missing";
7674
+ const BADGE_CLASS = "accessify-alt-badge";
7675
+ function getStyles() {
7676
+ return `
7677
+ .${HIGHLIGHT_CLASS} {
7678
+ outline: 3px dashed #e63946 !important;
7679
+ outline-offset: 3px !important;
7680
+ position: relative !important;
7681
+ transition: outline-color 0.2s ease !important;
7682
+ }
7683
+ .${HIGHLIGHT_CLASS}.accessify-alt-generating::after {
7684
+ content: '\\2026';
7685
+ position: absolute;
7686
+ top: 4px;
7687
+ left: 4px;
7688
+ padding: 2px 6px;
7689
+ background: #f77f00;
7690
+ color: #fff;
7691
+ font-family: system-ui, -apple-system, sans-serif;
7692
+ font-size: 10px;
7693
+ font-weight: 700;
7694
+ border-radius: 3px;
7695
+ line-height: 1.4;
7696
+ pointer-events: none;
7697
+ z-index: 1;
7698
+ }
7699
+ .${HIGHLIGHT_CLASS}.accessify-alt-done {
7700
+ outline-color: #2a9d8f !important;
7701
+ outline-style: solid !important;
7702
+ }
7703
+ .${HIGHLIGHT_CLASS}.accessify-alt-done::after {
7704
+ content: none;
7460
7705
  }
7461
7706
 
7462
- .accessify-body {
7463
- padding: var(--space-3);
7707
+ .accessify-alt-tooltip {
7708
+ position: fixed;
7709
+ z-index: 2147483647;
7710
+ max-width: 320px;
7711
+ padding: 8px 12px;
7712
+ background: #1a1a2e;
7713
+ color: #e0e0e0;
7714
+ border-radius: 8px;
7715
+ box-shadow: 0 4px 20px rgba(0,0,0,0.4);
7716
+ font-family: system-ui, -apple-system, sans-serif;
7717
+ font-size: 13px;
7718
+ line-height: 1.5;
7719
+ pointer-events: none;
7720
+ word-break: break-word;
7721
+ opacity: 1;
7464
7722
  }
7465
7723
 
7466
- .accessify-control-grid,
7467
- .accessify-features {
7468
- grid-template-columns: 1fr;
7724
+ .accessify-alt-info-btn {
7725
+ position: absolute;
7726
+ top: 6px;
7727
+ right: 6px;
7728
+ z-index: 10;
7729
+ width: 22px;
7730
+ height: 22px;
7731
+ padding: 0;
7732
+ border: none;
7733
+ border-radius: 50%;
7734
+ background: rgba(0,0,0,0.6);
7735
+ color: #ccc;
7736
+ font-family: system-ui, -apple-system, sans-serif;
7737
+ font-size: 13px;
7738
+ font-weight: 600;
7739
+ line-height: 22px;
7740
+ text-align: center;
7741
+ cursor: pointer;
7742
+ opacity: 0;
7743
+ transition: opacity 0.15s ease;
7744
+ backdrop-filter: blur(4px);
7745
+ }
7746
+ .${HIGHLIGHT_CLASS}.accessify-alt-done:hover + .accessify-alt-info-btn,
7747
+ .accessify-alt-info-btn:hover {
7748
+ opacity: 1;
7749
+ }
7750
+ .accessify-alt-info-btn:hover {
7751
+ background: rgba(0,0,0,0.8);
7752
+ color: #fff;
7469
7753
  }
7470
7754
 
7471
- .accessify-card {
7472
- min-height: 0;
7755
+ .accessify-alt-report-panel {
7756
+ position: fixed;
7757
+ z-index: 2147483646;
7758
+ width: 240px;
7759
+ padding: 12px;
7760
+ background: #1a1a2e;
7761
+ color: #f0f0f0;
7762
+ border-radius: 10px;
7763
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
7764
+ font-family: system-ui, -apple-system, sans-serif;
7765
+ font-size: 12px;
7766
+ line-height: 1.5;
7767
+ }
7768
+ .accessify-alt-report-panel p {
7769
+ margin: 0 0 8px 0;
7770
+ color: #aaa;
7771
+ }
7772
+ .accessify-alt-report-btn {
7773
+ display: inline-flex;
7774
+ align-items: center;
7775
+ gap: 4px;
7776
+ padding: 5px 10px;
7777
+ border: 1px solid rgba(220,53,69,0.3);
7778
+ border-radius: 6px;
7779
+ background: rgba(220,53,69,0.08);
7780
+ color: #f0a0a8;
7781
+ font-size: 11px;
7782
+ cursor: pointer;
7783
+ transition: background 0.15s ease;
7784
+ }
7785
+ .accessify-alt-report-btn:hover { background: rgba(220,53,69,0.2); }
7786
+ .accessify-alt-report-btn.reported {
7787
+ border-color: rgba(42,157,143,0.3);
7788
+ background: rgba(42,157,143,0.1);
7789
+ color: #2a9d8f;
7790
+ cursor: default;
7473
7791
  }
7474
7792
 
7475
- .accessify-trigger {
7476
- ${isBottom ? "bottom: 16px" : "top: 16px"};
7477
- ${isRight ? "right: 16px" : "left: 16px"};
7478
- width: 58px;
7479
- height: 58px;
7480
- border-radius: 20px;
7793
+ .${BADGE_CLASS} {
7794
+ position: fixed;
7795
+ top: 12px;
7796
+ right: 12px;
7797
+ z-index: 2147483646;
7798
+ display: flex;
7799
+ align-items: center;
7800
+ gap: 6px;
7801
+ padding: 8px 14px;
7802
+ background: #1a1a2e;
7803
+ color: #f0f0f0;
7804
+ border-radius: 8px;
7805
+ box-shadow: 0 4px 16px rgba(0,0,0,0.3);
7806
+ font-family: system-ui, -apple-system, sans-serif;
7807
+ font-size: 13px;
7808
+ user-select: none;
7809
+ }
7810
+ .${BADGE_CLASS}-count {
7811
+ display: inline-flex;
7812
+ align-items: center;
7813
+ justify-content: center;
7814
+ min-width: 22px;
7815
+ height: 22px;
7816
+ padding: 0 6px;
7817
+ background: #e63946;
7818
+ color: #fff;
7819
+ font-size: 12px;
7820
+ font-weight: 700;
7821
+ border-radius: 11px;
7822
+ }
7823
+ `;
7824
+ }
7825
+ function injectStyles() {
7826
+ if (document.getElementById(STYLE_ID)) return;
7827
+ styleEl = document.createElement("style");
7828
+ styleEl.id = STYLE_ID;
7829
+ styleEl.textContent = getStyles();
7830
+ document.head.appendChild(styleEl);
7831
+ }
7832
+ function removeStyles() {
7833
+ styleEl?.remove();
7834
+ styleEl = null;
7835
+ document.getElementById(STYLE_ID)?.remove();
7836
+ }
7837
+ function showTooltip(img) {
7838
+ const text = img.getAttribute("alt") || processedImages.get(img)?.generatedAlt;
7839
+ if (!text) return;
7840
+ hideTooltip();
7841
+ const tip = document.createElement("div");
7842
+ tip.className = "accessify-alt-tooltip";
7843
+ tip.textContent = text;
7844
+ document.body.appendChild(tip);
7845
+ tooltipEl = tip;
7846
+ const rect = img.getBoundingClientRect();
7847
+ const tipRect = tip.getBoundingClientRect();
7848
+ let top = rect.top - tipRect.height - 8;
7849
+ if (top < 4) top = rect.bottom + 8;
7850
+ let left = rect.left + (rect.width - tipRect.width) / 2;
7851
+ left = Math.max(4, Math.min(left, window.innerWidth - tipRect.width - 4));
7852
+ tip.style.top = `${top}px`;
7853
+ tip.style.left = `${left}px`;
7854
+ }
7855
+ function hideTooltip() {
7856
+ tooltipEl?.remove();
7857
+ tooltipEl = null;
7858
+ }
7859
+ function onMouseEnter(e) {
7860
+ showTooltip(e.currentTarget);
7861
+ }
7862
+ function onMouseLeave() {
7863
+ hideTooltip();
7864
+ }
7865
+ function addInfoButton(img) {
7866
+ if (infoButtons.has(img)) return;
7867
+ const parent = img.parentElement;
7868
+ if (parent) {
7869
+ const pos = getComputedStyle(parent).position;
7870
+ if (pos === "static") parent.style.position = "relative";
7871
+ }
7872
+ const btn = document.createElement("button");
7873
+ btn.className = "accessify-alt-info-btn";
7874
+ btn.textContent = "i";
7875
+ btn.setAttribute("aria-label", isDE() ? "Alt-Text melden" : "Report alt text");
7876
+ btn.addEventListener("click", (e) => {
7877
+ e.preventDefault();
7878
+ e.stopPropagation();
7879
+ showReportPanel(img, btn);
7880
+ });
7881
+ img.insertAdjacentElement("afterend", btn);
7882
+ infoButtons.set(img, btn);
7883
+ }
7884
+ function removeInfoButtons() {
7885
+ infoButtons.forEach((btn) => btn.remove());
7886
+ infoButtons.clear();
7887
+ }
7888
+ function showReportPanel(img, anchor) {
7889
+ closeReportPanel();
7890
+ const altText2 = img.getAttribute("alt") || "";
7891
+ const panel = document.createElement("div");
7892
+ panel.className = "accessify-alt-report-panel";
7893
+ const btnRect = anchor.getBoundingClientRect();
7894
+ panel.style.top = `${btnRect.bottom + 6}px`;
7895
+ panel.style.left = `${Math.min(btnRect.left, window.innerWidth - 260)}px`;
7896
+ const desc = document.createElement("p");
7897
+ desc.textContent = isDE() ? "Alt-Text falsch? Melden Sie den Fehler." : "Alt text wrong? Report the error.";
7898
+ panel.appendChild(desc);
7899
+ const reportBtn = document.createElement("button");
7900
+ reportBtn.className = "accessify-alt-report-btn";
7901
+ reportBtn.textContent = isDE() ? "⚠ Fehler melden" : "⚠ Report error";
7902
+ reportBtn.addEventListener("click", async () => {
7903
+ await saveReport(img.src, altText2, lang());
7904
+ await removeCachedAltText(img.src);
7905
+ reportBtn.textContent = isDE() ? "✓ Gemeldet" : "✓ Reported";
7906
+ reportBtn.classList.add("reported");
7907
+ });
7908
+ panel.appendChild(reportBtn);
7909
+ document.body.appendChild(panel);
7910
+ reportPanelEl = panel;
7911
+ setTimeout(() => {
7912
+ const handleOutside = (e) => {
7913
+ if (!panel.contains(e.target) && e.target !== anchor) {
7914
+ closeReportPanel();
7915
+ document.removeEventListener("click", handleOutside);
7916
+ }
7917
+ };
7918
+ document.addEventListener("click", handleOutside);
7919
+ }, 50);
7920
+ const handleEsc = (e) => {
7921
+ if (e.key === "Escape") {
7922
+ closeReportPanel();
7923
+ document.removeEventListener("keydown", handleEsc);
7924
+ }
7925
+ };
7926
+ document.addEventListener("keydown", handleEsc);
7927
+ }
7928
+ function closeReportPanel() {
7929
+ reportPanelEl?.remove();
7930
+ reportPanelEl = null;
7931
+ }
7932
+ function scanForMissingAlt() {
7933
+ const images = document.querySelectorAll("img");
7934
+ const missing = [];
7935
+ images.forEach((img) => {
7936
+ if (img.closest("#accessify-root")) return;
7937
+ if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 20 || img.naturalHeight < 20)) return;
7938
+ const alt = img.getAttribute("alt");
7939
+ if (alt === null || alt.trim() === "") missing.push(img);
7940
+ });
7941
+ return missing;
7942
+ }
7943
+ function applyAltText(img, altText2) {
7944
+ img.setAttribute("alt", altText2);
7945
+ img.setAttribute("title", altText2);
7946
+ processedImages.set(img, { generatedAlt: altText2 });
7947
+ if (enabled) {
7948
+ img.classList.add("accessify-alt-done");
7949
+ img.classList.remove("accessify-alt-generating");
7950
+ addInfoButton(img);
7951
+ }
7952
+ img.removeEventListener("mouseenter", onMouseEnter);
7953
+ img.removeEventListener("mouseleave", onMouseLeave);
7954
+ img.addEventListener("mouseenter", onMouseEnter);
7955
+ img.addEventListener("mouseleave", onMouseLeave);
7956
+ }
7957
+ async function generateAll() {
7958
+ if (autoGenerating || !enabled || !aiService) return;
7959
+ autoGenerating = true;
7960
+ const CONCURRENCY = 3;
7961
+ const uncached = [];
7962
+ for (const img of missingAltImages) {
7963
+ if (processedImages.has(img)) continue;
7964
+ const cached = await getCachedAltText(img.src);
7965
+ if (cached) {
7966
+ applyAltText(img, cached);
7967
+ } else {
7968
+ uncached.push(img);
7481
7969
  }
7482
7970
  }
7483
- `;
7971
+ updateBadge();
7972
+ if (!uncached.length) {
7973
+ autoGenerating = false;
7974
+ return;
7975
+ }
7976
+ uncached.forEach((img) => img.classList.add("accessify-alt-generating"));
7977
+ for (let i = 0; i < uncached.length; i += CONCURRENCY) {
7978
+ if (!enabled) break;
7979
+ const batch = uncached.slice(i, i + CONCURRENCY);
7980
+ await Promise.all(batch.map(async (img) => {
7981
+ if (!enabled) return;
7982
+ try {
7983
+ const ctx = gatherImageContext(img);
7984
+ const alt = await aiService.generateAltText(img.src, ctx, lang());
7985
+ if (alt && enabled) {
7986
+ applyAltText(img, alt);
7987
+ setCachedAltText(img.src, alt, lang()).catch(() => {
7988
+ });
7989
+ if (siteKey) persistAltTextToServer(siteKey, proxyUrl, img.src, alt, lang());
7990
+ }
7991
+ } catch (err) {
7992
+ console.warn("[Accessify] Alt-text generation failed:", img.src, err);
7993
+ img.classList.remove("accessify-alt-generating");
7994
+ }
7995
+ }));
7996
+ updateBadge();
7997
+ }
7998
+ autoGenerating = false;
7999
+ }
8000
+ async function generateSingle(img) {
8001
+ if (!aiService || !enabled) return;
8002
+ const cached = await getCachedAltText(img.src);
8003
+ if (cached) {
8004
+ applyAltText(img, cached);
8005
+ updateBadge();
8006
+ return;
8007
+ }
8008
+ img.classList.add("accessify-alt-generating");
8009
+ try {
8010
+ const ctx = gatherImageContext(img);
8011
+ const alt = await aiService.generateAltText(img.src, ctx, lang());
8012
+ if (alt && enabled) {
8013
+ applyAltText(img, alt);
8014
+ setCachedAltText(img.src, alt, lang()).catch(() => {
8015
+ });
8016
+ if (siteKey) persistAltTextToServer(siteKey, proxyUrl, img.src, alt, lang());
8017
+ updateBadge();
8018
+ }
8019
+ } catch (err) {
8020
+ console.warn("[Accessify] Alt-text generation failed:", img.src, err);
8021
+ img.classList.remove("accessify-alt-generating");
8022
+ }
8023
+ }
8024
+ function updateBadge() {
8025
+ removeBadge();
8026
+ const remaining = missingAltImages.filter((img) => !processedImages.has(img)).length;
8027
+ const generating = missingAltImages.filter((img) => img.classList.contains("accessify-alt-generating")).length;
8028
+ if (remaining === 0 && missingAltImages.length > 0) {
8029
+ showBadge(isDE() ? `✓ ${missingAltImages.length} Alt-Texte gesetzt` : `✓ ${missingAltImages.length} alt texts applied`, "#2a9d8f", 0);
8030
+ setTimeout(() => removeBadge(), 5e3);
8031
+ return;
8032
+ }
8033
+ if (remaining === 0) return;
8034
+ if (generating > 0) {
8035
+ showBadge(isDE() ? `${generating} Bilder werden analysiert…` : `Analyzing ${generating} images…`, "#f77f00", generating);
8036
+ } else {
8037
+ showBadge(isDE() ? `${remaining} Bilder ohne Alt-Text` : `${remaining} images missing alt text`, "#e63946", remaining);
8038
+ }
8039
+ }
8040
+ function showBadge(text, color, count) {
8041
+ const badge = document.createElement("div");
8042
+ badge.className = BADGE_CLASS;
8043
+ badge.setAttribute("role", "status");
8044
+ badge.setAttribute("aria-live", "polite");
8045
+ if (count > 0) {
8046
+ const c = document.createElement("span");
8047
+ c.className = `${BADGE_CLASS}-count`;
8048
+ c.style.background = color;
8049
+ c.textContent = String(count);
8050
+ badge.appendChild(c);
8051
+ }
8052
+ const label = document.createElement("span");
8053
+ label.textContent = text;
8054
+ badge.appendChild(label);
8055
+ document.body.appendChild(badge);
8056
+ badgeEl = badge;
8057
+ }
8058
+ function removeBadge() {
8059
+ badgeEl?.remove();
8060
+ badgeEl = null;
8061
+ }
8062
+ function gatherImageContext(img) {
8063
+ const parts = [];
8064
+ try {
8065
+ const url = new URL(img.src, window.location.href);
8066
+ const filename = url.pathname.split("/").pop();
8067
+ if (filename) parts.push(`Filename: ${filename}`);
8068
+ } catch {
8069
+ }
8070
+ const figure = img.closest("figure");
8071
+ if (figure) {
8072
+ const caption = figure.querySelector("figcaption");
8073
+ if (caption?.textContent?.trim()) parts.push(`Caption: ${caption.textContent.trim()}`);
8074
+ }
8075
+ const parent = img.parentElement;
8076
+ if (parent) {
8077
+ const heading = parent.querySelector("h1, h2, h3, h4, h5, h6");
8078
+ if (heading?.textContent?.trim()) parts.push(`Nearby heading: ${heading.textContent.trim()}`);
8079
+ }
8080
+ const describedBy = img.getAttribute("aria-describedby");
8081
+ if (describedBy) {
8082
+ const descEl = document.getElementById(describedBy);
8083
+ if (descEl?.textContent?.trim()) parts.push(`Description: ${descEl.textContent.trim()}`);
8084
+ }
8085
+ return parts.join(". ");
8086
+ }
8087
+ let domObserver = null;
8088
+ function tryRegisterImage(img) {
8089
+ if (!enabled) return;
8090
+ if (img.closest("#accessify-root")) return;
8091
+ if (missingAltImages.includes(img)) return;
8092
+ const alt = img.getAttribute("alt");
8093
+ if (alt !== null && alt.trim() !== "") return;
8094
+ function addIfValid() {
8095
+ if (!enabled || missingAltImages.includes(img)) return;
8096
+ if (img.naturalWidth >= 20 && img.naturalHeight >= 20) {
8097
+ img.classList.add(HIGHLIGHT_CLASS);
8098
+ missingAltImages.push(img);
8099
+ updateBadge();
8100
+ generateSingle(img);
8101
+ }
8102
+ }
8103
+ if (img.complete && img.naturalWidth > 0) addIfValid();
8104
+ else img.addEventListener("load", addIfValid, { once: true });
8105
+ }
8106
+ function activate() {
8107
+ if (enabled) return;
8108
+ enabled = true;
8109
+ injectStyles();
8110
+ missingAltImages = scanForMissingAlt();
8111
+ missingAltImages.forEach((img) => img.classList.add(HIGHLIGHT_CLASS));
8112
+ updateBadge();
8113
+ generateAll();
8114
+ document.querySelectorAll("img").forEach((img) => {
8115
+ if (!img.complete) tryRegisterImage(img);
8116
+ });
8117
+ setTimeout(() => {
8118
+ if (enabled) document.querySelectorAll("img").forEach(tryRegisterImage);
8119
+ }, 2e3);
8120
+ domObserver = new MutationObserver((mutations) => {
8121
+ for (const m of mutations) {
8122
+ for (const node of m.addedNodes) {
8123
+ if (node instanceof HTMLImageElement) tryRegisterImage(node);
8124
+ else if (node instanceof HTMLElement) node.querySelectorAll("img").forEach(tryRegisterImage);
8125
+ }
8126
+ }
8127
+ });
8128
+ domObserver.observe(document.body, { childList: true, subtree: true });
8129
+ }
8130
+ function deactivate() {
8131
+ enabled = false;
8132
+ autoGenerating = false;
8133
+ domObserver?.disconnect();
8134
+ domObserver = null;
8135
+ hideTooltip();
8136
+ closeReportPanel();
8137
+ removeInfoButtons();
8138
+ removeBadge();
8139
+ missingAltImages.forEach((img) => {
8140
+ img.classList.remove(HIGHLIGHT_CLASS, "accessify-alt-done", "accessify-alt-generating");
8141
+ });
8142
+ missingAltImages = [];
8143
+ removeStyles();
8144
+ }
8145
+ autoApplyCachedAltTexts().catch(() => {
8146
+ });
8147
+ return {
8148
+ id: "alt-text",
8149
+ name: () => isDE() ? "Alt-Text erzeugen" : "Alt-Text Generator",
8150
+ description: isDE() ? "Bildbeschreibungen automatisch erstellen" : "Auto-generate image descriptions",
8151
+ icon: "alt-text",
8152
+ category: "ai",
8153
+ activate,
8154
+ deactivate,
8155
+ getState: () => ({
8156
+ id: "alt-text",
8157
+ enabled,
8158
+ value: {
8159
+ missingCount: missingAltImages.length,
8160
+ processedCount: processedImages.size
8161
+ }
8162
+ })
8163
+ };
7484
8164
  }
8165
+ const altText = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
8166
+ __proto__: null,
8167
+ autoApplyCachedAltTexts,
8168
+ default: createAltTextModule
8169
+ }, Symbol.toStringTag, { value: "Module" }));
7485
8170
  const REMOVED_FEATURES = /* @__PURE__ */ new Set([
7486
8171
  "spacing",
7487
8172
  "dyslexia-font",
@@ -7489,7 +8174,10 @@ const REMOVED_FEATURES = /* @__PURE__ */ new Set([
7489
8174
  "color-blindness",
7490
8175
  "saturation",
7491
8176
  "text-align",
7492
- "line-height"
8177
+ "line-height",
8178
+ "page-structure",
8179
+ "focus-highlight",
8180
+ "auto-scan"
7493
8181
  ]);
7494
8182
  const DEFAULT_CONFIG = {
7495
8183
  position: "bottom-right",
@@ -7502,15 +8190,12 @@ const DEFAULT_CONFIG = {
7502
8190
  "hide-images",
7503
8191
  "reading-guide",
7504
8192
  "reading-mask",
7505
- "focus-highlight",
7506
8193
  "big-cursor",
7507
8194
  "keyboard-nav",
7508
- "page-structure",
7509
8195
  "animation-stop",
7510
8196
  "tts",
7511
8197
  "text-simplify",
7512
- "alt-text",
7513
- "auto-scan"
8198
+ "alt-text"
7514
8199
  ],
7515
8200
  zIndex: 999999,
7516
8201
  compact: false
@@ -7518,10 +8203,52 @@ const DEFAULT_CONFIG = {
7518
8203
  let widgetInstance = null;
7519
8204
  let containerEl = null;
7520
8205
  let config = { ...DEFAULT_CONFIG };
7521
- function init(userConfig = {}) {
8206
+ async function fetchSiteConfig(siteKey, proxyUrl) {
8207
+ try {
8208
+ const base = proxyUrl || "https://accessify-api.accessify.workers.dev";
8209
+ const res = await fetch(`${base}/v1/config/${siteKey}`, {
8210
+ headers: { "Accept": "application/json" }
8211
+ });
8212
+ if (!res.ok) return {};
8213
+ const data = await res.json();
8214
+ const serverCfg = data?.config || {};
8215
+ const mapped = {};
8216
+ if (serverCfg.features?.length) mapped.features = serverCfg.features;
8217
+ if (serverCfg.defaultLang) mapped.lang = serverCfg.defaultLang;
8218
+ if (serverCfg.position) mapped.position = serverCfg.position;
8219
+ if (serverCfg.theme) mapped.theme = serverCfg.theme;
8220
+ if (serverCfg.compact != null) mapped.compact = serverCfg.compact;
8221
+ const brandFields = [
8222
+ "brandPrimaryColor",
8223
+ "brandSurfaceColor",
8224
+ "brandTextColor",
8225
+ "brandBorderColor",
8226
+ "brandCardColor",
8227
+ "brandAccentColor",
8228
+ "brandTextDimColor",
8229
+ "brandTriggerColor",
8230
+ "brandTriggerIconColor",
8231
+ "brandHoverColor"
8232
+ ];
8233
+ for (const field of brandFields) {
8234
+ if (serverCfg[field]) mapped[field] = serverCfg[field];
8235
+ }
8236
+ if (serverCfg.panelWidth) mapped.panelWidth = serverCfg.panelWidth;
8237
+ return mapped;
8238
+ } catch {
8239
+ return {};
8240
+ }
8241
+ }
8242
+ async function init(userConfig = {}) {
7522
8243
  if (widgetInstance) return;
7523
8244
  const dataConfig = window.__accessify_dataConfig || {};
7524
8245
  config = { ...DEFAULT_CONFIG, ...dataConfig, ...userConfig };
8246
+ const siteKey = config.siteKey || config.siteKey;
8247
+ const proxyUrl = config.proxyUrl || config.proxyUrl;
8248
+ if (siteKey) {
8249
+ const serverConfig = await fetchSiteConfig(siteKey, proxyUrl || "");
8250
+ config = { ...DEFAULT_CONFIG, ...serverConfig, ...dataConfig, ...userConfig };
8251
+ }
7525
8252
  config.features = (config.features || DEFAULT_CONFIG.features).filter((feature) => !REMOVED_FEATURES.has(feature));
7526
8253
  containerEl = document.createElement("div");
7527
8254
  containerEl.id = "accessify-root";
@@ -7562,7 +8289,29 @@ function init(userConfig = {}) {
7562
8289
  if (handled) e.stopImmediatePropagation();
7563
8290
  }, true);
7564
8291
  }
8292
+ window.addEventListener("accessify:reposition", ((e) => {
8293
+ const newPos = e.detail?.position;
8294
+ if (newPos && config) {
8295
+ config.position = newPos;
8296
+ sheet.replaceSync(createWidgetStyles(config));
8297
+ }
8298
+ }));
8299
+ window.addEventListener("message", (e) => {
8300
+ if (e.data?.type === "accessify:preview-config") {
8301
+ const preview = e.data.config || {};
8302
+ Object.assign(config, preview);
8303
+ sheet.replaceSync(createWidgetStyles(config));
8304
+ }
8305
+ if (e.data?.type === "accessify:open") {
8306
+ const panel = shadow.querySelector(".accessify-panel");
8307
+ const trigger = shadow.querySelector(".accessify-trigger");
8308
+ if (panel) panel.setAttribute("aria-hidden", "false");
8309
+ if (trigger) trigger.setAttribute("aria-expanded", "true");
8310
+ }
8311
+ });
7565
8312
  document.body.appendChild(containerEl);
8313
+ autoApplyCachedAltTexts().catch(() => {
8314
+ });
7566
8315
  config.onReady?.();
7567
8316
  }
7568
8317
  function destroy() {
@@ -7588,16 +8337,20 @@ if (api?.q) {
7588
8337
  api.q.forEach((args) => exec(...args));
7589
8338
  api.q = [];
7590
8339
  }
7591
- window.Accessify = function(cmd, ...args) {
8340
+ const finalApi = function(cmd, ...args) {
7592
8341
  exec(cmd, ...args);
7593
8342
  };
7594
- window.Accessify._loaded = true;
7595
- window.A11yPlus = window.Accessify;
8343
+ finalApi._loaded = true;
8344
+ Object.defineProperty(finalApi, "config", {
8345
+ get: () => config,
8346
+ enumerable: true
8347
+ });
8348
+ window.Accessify = finalApi;
8349
+ window.A11yPlus = finalApi;
7596
8350
  export {
7597
- RateLimitError as R,
7598
8351
  destroy as d,
7599
8352
  getCurrentWidgetLang as g,
7600
8353
  init as i,
7601
8354
  t
7602
8355
  };
7603
- //# sourceMappingURL=index-B6b-ij4T.js.map
8356
+ //# sourceMappingURL=index-i-tNypvI.js.map