Plugin Directory

Changeset 3462162


Ignore:
Timestamp:
02/16/2026 02:46:47 AM (5 days ago)
Author:
berlinlion
Message:

Release 1.0.50

Location:
a11ybridge/trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • a11ybridge/trunk/a11ybridge-plugin.php

    r3461477 r3462162  
    44 * Plugin URI:        https://a11ybridge.de
    55 * Description:       Accessibility toolbar for WordPress: font size, contrast, focus mode, color-blind filters, keyboard navigation, text-to-speech, and optional AI text simplification.
    6  * Version:           1.0.46
     6 * Version:           1.0.50
    77 * Requires at least: 6.0
    88 * Tested up to:      6.9
     
    5252// 1) Plugin release version (already defined by you from plugin header)
    5353if (!defined('A11YBRIDGE_VERSION')) {
    54     define('A11YBRIDGE_VERSION', '1.0.46'); // fallback, should never be used
     54    define('A11YBRIDGE_VERSION', '1.0.50'); // fallback, should never be used
    5555}
    5656
     
    107107}
    108108
     109/**
     110 * Cloud / external service consent (opt-in).
     111 *
     112 * WP.org policy: the plugin must not phone home or register domains automatically.
     113 * Cloud requests are only performed after explicit admin opt-in.
     114 * (Entering/activating a license is treated as an explicit admin action, but does not automatically enable cloud processing for visitors.)
     115 */
     116if (!function_exists('a11ybridge_get_cloud_settings')) {
     117    function a11ybridge_get_cloud_settings(): array {
     118        $opt = get_option('a11ybridge_cloud_settings', []);
     119        return is_array($opt) ? $opt : [];
     120    }
     121}
     122
     123if (!function_exists('a11ybridge_cloud_enabled')) {
     124    function a11ybridge_cloud_enabled(): bool {
     125        $opt = a11ybridge_get_cloud_settings();
     126        return !empty($opt['enabled']);
     127    }
     128}
     129
     130if (!function_exists('a11ybridge_any_ai_feature_enabled')) {
     131    function a11ybridge_any_ai_feature_enabled(): bool {
     132        $ai = get_option('a11ybridge_ai_settings', []);
     133        $simpl = get_option('a11ybridge_simplification_settings', []);
     134        if (!is_array($ai)) { $ai = []; }
     135        if (!is_array($simpl)) { $simpl = []; }
     136
     137        // WP.org build: MVP cloud features.
     138        return (!empty($ai['enable_ai_image_description']) || !empty($simpl['enable_text_simplification']));
     139    }
     140}
     141
     142if (!function_exists('a11ybridge_text_simplification_admin_enabled')) {
     143    function a11ybridge_text_simplification_admin_enabled(): bool {
     144        $simpl = get_option('a11ybridge_simplification_settings', []);
     145        if (!is_array($simpl)) { $simpl = []; }
     146        return !empty($simpl['enable_text_simplification']);
     147    }
     148}
     149
     150if (!function_exists('a11ybridge_text_simplification_allowed')) {
     151    function a11ybridge_text_simplification_allowed(): bool {
     152        // Requirement: admin must explicitly enable cloud services AND the feature.
     153        return a11ybridge_cloud_enabled() && a11ybridge_text_simplification_admin_enabled();
     154    }
     155}
     156
     157if (!function_exists('a11ybridge_has_paid_license')) {
     158    function a11ybridge_has_paid_license(): bool {
     159        $pro = get_option('a11ybridge_pro_license_settings', []);
     160        if (!is_array($pro)) { $pro = []; }
     161
     162        $license_hash = !empty($pro['license_hash']) ? (string) $pro['license_hash'] : '';
     163        if ($license_hash === '') {
     164            $license_hash = (string) get_option('a11yb_license_key_hash', '');
     165        }
     166
     167        $plan = !empty($pro['plan']) ? strtolower(trim((string) $pro['plan'])) : '';
     168        return ($license_hash !== '' && $plan !== 'free');
     169    }
     170}
     171
     172if (!function_exists('a11ybridge_cloud_opted_in')) {
     173    function a11ybridge_cloud_opted_in(): bool {
     174        // Explicit opt-in only (WP.org compliance): do not treat feature toggles as consent.
     175        return a11ybridge_cloud_enabled();
     176    }
     177}
     178
    109179register_activation_hook(__FILE__, function () {
    110180    // Ensure install_id exists early.
     
    123193
    124194    $content  = '<h2>A11yBridge</h2>';
    125     $content .= '<p>A11yBridge is an accessibility plugin. Some optional features may send content to A11yBridge servers (https://a11ybridge.de) for processing (for example: AI text simplification), and return the result to the site visitor.</p>';
     195    $content .= '<p>A11yBridge is an accessibility plugin. Some optional cloud features may send content to A11yBridge servers (https://a11ybridge.de) for processing (for example: AI text simplification), and return the result to the site visitor. Cloud features are disabled by default and are only used after the site owner enables them.</p>';
    126196    $content .= '<p>When a cloud feature is used, the selected text and the chosen settings (e.g. language and simplification level) are transmitted for processing.</p>';
    127197    $content .= '<p>A11yBridge does not use analytics cookies. The plugin may store accessibility preferences locally (e.g. browser storage) and may set a session cookie for guest users if needed to persist settings between requests.</p>';
     
    535605        'nonce' => wp_create_nonce('a11ybridge_settings_nonce'),
    536606        'isUserLoggedIn'  => is_user_logged_in(),
     607        'cloudEnabled'   => (int) a11ybridge_cloud_enabled(),
     608        'adminTextSimplificationEnabled' => (int) a11ybridge_text_simplification_admin_enabled(),
    537609        'simplifyProxyUrl' => esc_url_raw( rest_url('a11ybridge/v1/ai/simplify') ),
    538610        'aiTokenUrl'      => esc_url_raw( rest_url('a11ybridge/v1/ai/token') ),
     
    685757    }
    686758
     759    // If cloud services are not enabled, do not issue tokens (no AI without explicit opt-in).
     760    if (function_exists('a11ybridge_text_simplification_allowed') && !a11ybridge_text_simplification_allowed()) {
     761        return new WP_REST_Response([ 'ok' => false, 'error' => 'cloud_or_feature_disabled' ], 403);
     762    }
     763
    687764    $token = a11ybridge_ai_proxy_issue_token();
    688765
     
    694771
    695772function a11ybridge_rest_ai_simplify_permission( WP_REST_Request $req ) {
     773    // Admin must explicitly enable cloud services AND the feature (double opt-in).
     774    if (function_exists('a11ybridge_text_simplification_allowed') && !a11ybridge_text_simplification_allowed()) {
     775        return new WP_Error('a11ybridge_cloud_disabled', 'Cloud services are disabled. Enable Cloud connection in the plugin settings first.', [ 'status' => 403 ]);
     776    }
     777
     778    $simpl = get_option('a11ybridge_simplification_settings', []);
     779    if (!is_array($simpl) || empty($simpl['enable_text_simplification'])) {
     780        return new WP_Error('a11ybridge_feature_disabled', 'AI text simplification is disabled by the site administrator', [ 'status' => 403 ]);
     781    }
    696782    $token = sanitize_text_field( (string) $req->get_header( 'x-a11yb-token' ) );
    697783    if ( $token === '' ) {
     
    9571043        'nonce'           => wp_create_nonce('a11ybridge_settings_nonce'),
    9581044        'isUserLoggedIn'  => is_user_logged_in(),
     1045         'cloudEnabled'   => (int) a11ybridge_cloud_enabled(),
     1046        'adminTextSimplificationEnabled' => (int) a11ybridge_text_simplification_admin_enabled(),
    9591047        'simplifyProxyUrl' => esc_url_raw( rest_url('a11ybridge/v1/ai/simplify') ),
    9601048        'aiTokenUrl'      => esc_url_raw( rest_url('a11ybridge/v1/ai/token') ),
  • a11ybridge/trunk/admin/settings-pro.php

    r3461470 r3462162  
    2626
    2727function a11ybridge_show_pro_settings() {
    28     if (class_exists('A11YB_License_API')) {
    29         // Ignore errors, UI will fall back to defaults if needed
    30         A11YB_License_API::refresh_status_and_options([]);
    31     }
    32 
     28   
    3329    // 1) Load local license options
    3430    $opt = get_option('a11ybridge_pro_license_settings', []);
     
    3632        $opt = [];
    3733    }
     34
     35     // Cloud consent (opt-in)
     36    $cloud = get_option('a11ybridge_cloud_settings', []);
     37    if (!is_array($cloud)) {
     38        $cloud = [];
     39    }
     40    $cloud_enabled   = !empty($cloud['enabled']);
     41    $telemetry_optin = !empty($cloud['telemetry']);
    3842
    3943    // Current domain
     
    4852    $bound_domain = $opt['bound_domain'] ?? '';
    4953
     54    // Fetch remote quota/status only after explicit opt-in (or if a paid license is present).
     55    if (class_exists('A11YB_License_API')) {
     56        try {
     57            if (function_exists('a11ybridge_cloud_opted_in') ? a11ybridge_cloud_opted_in() : false) {
     58                A11YB_License_API::refresh_status_and_options(['screen' => 'admin_pro_tab']);
     59            }
     60        } catch (\Throwable $e) {
     61            // ignore
     62        }
     63    }
     64
    5065    // "active" = hash + some bound domain according to backend
    5166    $has_hash = !empty($license_hash) && !empty($bound_domain);
     
    6782// Global MVP counters (replace these option keys later with real sources)
    6883$plan_level     = $plan; // free|paid|unknown
    69 
    70 if (!empty($license_hash)) {
    71     try {
    72         \A11YB_License_API::refresh_status_and_options(['screen' => 'admin_pro_tab']);
    73     } catch (\Throwable $e) {}
    74 }
    7584
    7685$reset_ts = (int) get_option('a11yb_next_update_at_ts', 0);
     
    139148
    140149          <!-- =========================
     150           Cloud connection (opt-in)
     151               ========================= -->
     152          <div class="a11y-settings-section" style="border-left: 4px solid #2271b1;">
     153            <h2><?php esc_html_e('Cloud connection (optional)', 'a11ybridge'); ?></h2>
     154            <p class="description">
     155              <?php esc_html_e('A11yBridge can optionally connect to A11yBridge cloud services for AI features and quota/licensing. For WP.org compliance, no external requests are made until you enable cloud services (or activate a license).', 'a11ybridge'); ?>
     156            </p>
     157
     158            <label style="display:block; margin: 8px 0;">
     159              <input type="hidden" name="a11ybridge_cloud_settings[enabled]" value="0" />
     160              <input type="checkbox" id="a11ybridge-cloud-enabled" name="a11ybridge_cloud_settings[enabled]" value="1" <?php checked($cloud_enabled); ?> />
     161              <?php esc_html_e('Enable cloud services for this site', 'a11ybridge'); ?>
     162            </label>
     163
     164            <label style="display:block; margin: 8px 0;">
     165              <input type="hidden" name="a11ybridge_cloud_settings[telemetry]" value="0" />
     166              <input type="checkbox" id="a11ybridge-cloud-telemetry" name="a11ybridge_cloud_settings[telemetry]" value="1" <?php checked($telemetry_optin); ?> />
     167              <?php esc_html_e('Share anonymous diagnostics (optional)', 'a11ybridge'); ?>
     168            </label>
     169
     170            <div id="a11ybridge-cloud-save-status" class="description" style="display:none; margin-top: 8px;"></div>
     171
     172            <?php if (!$cloud_enabled && empty($license_hash)): ?>
     173              <p class="description" style="margin-top: 8px; color:#8a2424;">
     174                <?php esc_html_e('Cloud services are currently disabled. AI features that require external processing will not be available until you enable cloud services.', 'a11ybridge'); ?>
     175              </p>
     176            <?php endif; ?>
     177          </div>
     178
     179          <?php
     180// Auto-save cloud consent toggles (no submit button on this tab).
     181ob_start();
     182?>
     183(function(){
     184  const enabledCb   = document.getElementById('a11ybridge-cloud-enabled');
     185  const telemetryCb = document.getElementById('a11ybridge-cloud-telemetry');
     186  const statusEl    = document.getElementById('a11ybridge-cloud-save-status');
     187  if (!enabledCb || !telemetryCb || !statusEl) return;
     188
     189  const ajaxUrl = (window.A11YBRIDGE_ADMIN && window.A11YBRIDGE_ADMIN.ajaxUrl) ? window.A11YBRIDGE_ADMIN.ajaxUrl : '';
     190  const nonce   = (window.A11YBRIDGE_ADMIN && window.A11YBRIDGE_ADMIN.noncePro) ? window.A11YBRIDGE_ADMIN.noncePro : '';
     191  if (!ajaxUrl || !nonce) return;
     192
     193  function setStatus(msg, isOk){
     194    if (!statusEl) return;
     195    statusEl.style.display = 'block';
     196    statusEl.style.color = isOk ? '#0f5132' : '#8a2424';
     197    statusEl.textContent = msg;
     198    clearTimeout(setStatus._t);
     199    setStatus._t = setTimeout(()=>{ statusEl.style.display='none'; }, 2500);
     200  }
     201
     202  function syncTelemetryEnabledState(){
     203    const cloudEnabled = !!enabledCb.checked;
     204    telemetryCb.disabled = !cloudEnabled;
     205    if (!cloudEnabled && telemetryCb.checked){
     206      telemetryCb.checked = false;
     207    }
     208  }
     209
     210  async function save(){
     211    syncTelemetryEnabledState();
     212
     213    const enabled   = enabledCb.checked ? 1 : 0;
     214    const telemetry = (!telemetryCb.disabled && telemetryCb.checked) ? 1 : 0;
     215    const body = new URLSearchParams();
     216    body.set('action', 'a11ybridge_save_cloud_settings');
     217    body.set('nonce', nonce);
     218    body.set('enabled', String(enabled));
     219    body.set('telemetry', String(telemetry));
     220
     221    try{
     222      const res = await fetch(ajaxUrl, {
     223        method: 'POST',
     224        headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
     225        body: body.toString(),
     226        credentials: 'same-origin'
     227      });
     228      const data = await res.json().catch(()=>null);
     229      if (!res.ok || !data || !data.success){
     230        const msg = (data && data.data && (data.data.message || data.data.msg)) ? (data.data.message || data.data.msg) : 'Save failed.';
     231        setStatus(msg, false);
     232        return;
     233      }
     234      // normalize server response
     235      if (typeof data.data?.enabled !== 'undefined'){
     236        enabledCb.checked = !!data.data.enabled;
     237      }
     238      if (typeof data.data?.telemetry !== 'undefined'){
     239        telemetryCb.checked = !!data.data.telemetry;
     240      }
     241      syncTelemetryEnabledState();
     242      setStatus('Saved.', true);
     243    } catch(e){
     244      setStatus('Save failed (network).', false);
     245    }
     246  }
     247
     248  enabledCb.addEventListener('change', save);
     249  telemetryCb.addEventListener('change', save);
     250  syncTelemetryEnabledState();
     251})();
     252<?php
     253$__js = ob_get_clean();
     254wp_add_inline_script('a11ybridge-admin-inline', $__js, 'after');
     255?>
     256
     257<!-- =========================
    141258               Section 1: Overall statistics
    142259               ========================= -->
     
    213330            </p>
    214331
     332            <?php if ($show_account_links && ($cloud_enabled || !empty($license_hash))): ?>
    215333            <p class="description" style="margin: 12px 0;">
    216334                <?php esc_html_e('Need more AI quota? Manage your subscription in your account dashboard.', 'a11ybridge'); ?>
     
    219337                </a>
    220338            </p>
     339            <?php endif; ?>
    221340        </div>
    222341
     
    589708                        // If your endpoint needs a nonce etc., add it here
    590709                    },
    591                     body: JSON.stringify({ meta: {} })
     710                    body: JSON.stringify({ meta: { user_action: true } })
    592711                });
    593712
  • a11ybridge/trunk/admin/settings.php

    r3461470 r3462162  
    4646    // Pro settings
    4747    register_setting('a11ybridge_pro_settings_group', 'a11ybridge_pro_license_settings', ['type' => 'array', 'sanitize_callback' => 'a11ybridge_sanitize_option_array']);
     48    // Cloud / external service consent (opt-in)
     49    register_setting('a11ybridge_pro_settings_group', 'a11ybridge_cloud_settings', ['type' => 'array', 'sanitize_callback' => 'a11ybridge_sanitize_option_array']);
    4850}
    4951
     
    5153add_action('wp_ajax_a11ybridge_save_license_hash', 'a11ybridge_ajax_save_license_hash');
    5254add_action('wp_ajax_a11ybridge_clear_license', 'a11ybridge_ajax_clear_license');
    53 
     55add_action('wp_ajax_a11ybridge_save_cloud_settings', 'a11ybridge_ajax_save_cloud_settings');
    5456/**
    5557 * AJAX handler: store license hash and metadata locally.
     
    169171
    170172/**
     173 * AJAX handler: save cloud consent settings (opt-in) immediately.
     174 *
     175 * WP.org compliance note:
     176 * - This stores settings locally in WordPress options.
     177 * - It does NOT contact external services by itself.
     178 */
     179function a11ybridge_ajax_save_cloud_settings() {
     180    if (!current_user_can('manage_options')) {
     181        wp_send_json_error(
     182            [
     183                'msg'     => 'forbidden',
     184                'message' => __('You are not allowed to perform this action.', 'a11ybridge'),
     185            ],
     186            403
     187        );
     188    }
     189    // Nonce (matches window.A11YBRIDGE_ADMIN.noncePro)
     190    check_ajax_referer('a11ybridge_pro_nonce', 'nonce');
     191
     192    $enabled   = isset($_POST['enabled']) ? (int) sanitize_text_field(wp_unslash($_POST['enabled'])) : 0;
     193    $telemetry = isset($_POST['telemetry']) ? (int) sanitize_text_field(wp_unslash($_POST['telemetry'])) : 0;
     194    $enabled   = $enabled ? 1 : 0;
     195    // If cloud is disabled, telemetry must be disabled too.
     196    $telemetry = ($enabled && $telemetry) ? 1 : 0;
     197
     198    $opt = get_option('a11ybridge_cloud_settings', []);
     199    if (!is_array($opt)) {
     200        $opt = [];
     201    }
     202
     203    $opt['enabled']   = $enabled;
     204    $opt['telemetry'] = $telemetry;
     205    $opt['updated_at'] = current_time('mysql');
     206
     207    update_option('a11ybridge_cloud_settings', $opt, false);
     208
     209    wp_send_json_success([
     210        'ok'        => true,
     211        'enabled'   => $enabled,
     212        'telemetry' => $telemetry,
     213        'message'   => __('Cloud settings saved.', 'a11ybridge'),
     214    ]);
     215}
     216
     217/**
    171218 * Add admin menu entry for A11yBridge settings.
    172219 */
  • a11ybridge/trunk/frontend/assets/js/text-simplification.js

    r3461470 r3462162  
    2222   
    2323    async init() {
     24      // Hard gate: do not expose UI or make requests unless admin enabled cloud + feature.
     25        const cfg = window.a11ybridgePlugin || {};
     26        if (!cfg.cloudEnabled || !cfg.adminTextSimplificationEnabled) {
     27            globalThis.A11yBridgeLogger.debug('🛑 Text Simplification disabled (cloud or admin feature toggle off).');
     28            return;
     29        }
     30
    2431        await this.loadSavedSettings();
    2532        this.setupTextSelection();
  • a11ybridge/trunk/includes/class-a11ybridge-license.php

    r3461470 r3462162  
    6565            'license_hash' => $key_hash, // optional
    6666            'domain'       => $domain,
    67             'site_url'     => $site_url,
    6867            'wp_version'   => get_bloginfo('version'),
    6968            'plugin_ver'   => A11YBRIDGE_VERSION,
    70             'admin_email'  => get_option('admin_email'),
    7169            'meta'         => self::sanitize_meta($meta),
    7270        ];
     
    256254            }
    257255
     256            // WP.org compliance: do not contact external servers unless the site owner opted in
     257            // (e.g. explicitly enabling cloud services in the Pro tab).
     258            $cloud_ok = (function_exists('a11ybridge_cloud_opted_in') && a11ybridge_cloud_opted_in());
     259            $user_action = !empty($meta['user_action']);
     260            if (!$cloud_ok && !$user_action) {
     261                // Keep local counters only (no remote call).
     262                $fallback_limit = (int) get_option('a11yb_monthly_limit', 100);
     263                if ($fallback_limit <= 0) { $fallback_limit = 100; }
     264                $local_used = max(0, (int) get_option('a11yb_monthly_used', 0));
     265                $local_remaining = max(0, $fallback_limit - $local_used);
     266
     267                update_option('a11yb_plan_level', 'free', false);
     268                update_option('a11yb_monthly_limit', $fallback_limit, false);
     269                update_option('a11yb_ai_credits_remaining', $local_remaining, false);
     270
     271                return [
     272                    'plan'          => 'free',
     273                    'monthly_limit' => $fallback_limit,
     274                    'monthly_used'  => $local_used,
     275                    'remaining'     => $local_remaining,
     276                    'status'        => 'local_opt_out',
     277                ];
     278            }
     279
    258280            $payload = [
    259281                'installation_id' => $install_id,
    260282                'domain'          => $domain,
    261                 'site_url'        => $site_url,
    262283                'meta'            => self::sanitize_meta($meta),
    263284            ];
     
    266287            $resp = self::remote_json('/free/status', $payload, [
    267288                'X-AB-Installation-Id' => $install_id,
    268                 'X-AB-Origin'          => home_url('/'),
    269289            ]);
    270290        } else {
     
    272292                'license_hash' => $license_hash,
    273293                'domain'       => $domain,
    274                 'site_url'     => $site_url,
    275294                'meta'         => self::sanitize_meta($meta),
    276295            ];
  • a11ybridge/trunk/readme.txt

    r3461477 r3462162  
    22Contributors: berlinlion
    33Donate link: https://a11ybridge.de/
    4 Tags: accessibility, wcag, contrast, text-to-speech, keyboard
     4Tags: accessibility, wcag, contrast, text-to-speech, keyboard, a11y, screen reader, web-accessibility
    55Requires at least: 6.0
    6 Tested up to: 6.9
     6Tested up to: 6.9.1
    77Requires PHP: 7.4
    8 Stable tag: 1.0.46
     8Stable tag: 1.0.50
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1111
    12 Accessibility toolbar: font size, contrast, focus mode, keyboard support, text-to-speech, and optional AI text simplification.
     12Accessibility toolbar for WordPress: font size, contrast, focus mode, color filters, keyboard navigation, text-to-speech, plus optional AI text simplification.
    1313
    1414
     
    3232== AI features (optional) ==
    3333
    34 AI text simplification is processed on external servers. Any usage limits are enforced by the external service and depend on the service subscription associated with the installation.
     34AI text simplification is processed on external servers. Cloud features are disabled by default and are only used after the site owner enables them (or activates a license). Any usage limits are enforced by the external service and depend on the service subscription associated with the installation.
    3535All non-AI accessibility tools (toolbar, contrast, focus mode, keyboard support, text-to-speech) work without any AI connection.
    3636
    37 AI text simplification is processed on external servers. Any usage limits are enforced by the external service and depend on the service plan associated with the installation.
    3837The plugin itself does not lock or restrict built-in/local functionality based on a license key, trial period, or local usage counters.
    3938If the service quota is exceeded, the service returns an error response and the plugin displays a notice.
     
    4140== External Services / Data Transmission ==
    4241
    43 This plugin may connect to A11yBridge backend services to provide optional AI text simplification and service operations (e.g., quota management, abuse prevention).
     42This plugin can optionally connect to A11yBridge backend services to provide AI text simplification and related service operations (e.g., quota management, license activation, abuse prevention). For WP.org compliance, it does NOT contact external servers by default. External requests happen only after explicit admin opt-in (cloud enabled / AI feature enabled) or after the admin activates a license key.
    4443
    4544Service endpoint(s):
     
    4948  - /license/activate (activate/bind license to this site)
    5049  - /license/domains/delete (remove domain binding from license)
     50  - /free/status (free-tier quota status, only after admin opt-in)
    5151  - /chat/simplify (AI text simplification, only when triggered by the user)
    5252
     
    7070* Selected text submitted by the user for AI text simplification (only when the user triggers simplification)
    7171* Pseudonymous installation ID
    72 * Domain/host
     72* Domain/host (only after admin opt-in or license activation)
    7373* License key hash (pseudonymous, if a license is used)
    7474* Service-side usage/quota accounting
     
    131131== Screenshots ==
    132132
    133 1. Frontend Toolbar opened (mobil)
    134 2. Admin Dashboard Overview (Basic Features)
    135 3. Admin AI Settings (inkl. Consent)
    136 4. Font/Text-Size Settings Dialog
    137 5. Color Adjustments (High Contrast)
    138 6. Focus Mode Dialog
    139 7. Keyboard Navigation Dialog
    140 8. Color Filters Dialog
    141 8. Text-to-Speech Dialog
    142 10. Simplify Result Modal
     1331. Frontend toolbar (mobile)
     1342. Admin dashboard overview (basic features)
     1353. Admin AI settings (including consent)
     1364. Font and text size controls
     1375. High contrast mode
     1386. Focus mode
     1397. Keyboard navigation and focus indicators
     1408. Color filters and adjustments
     1419. Text-to-speech settings
     14210. AI text simplification result
    143143
    144144== Changelog ==
    145145
    146 = 1.0.46 =
     146= 1.0.50 =
    147147* WP.org compliance fixes: removed trialware indicators, removed legacy frontend templates with direct script/style tags.
    148148* Fixed undefined function error in a11ybridge-plugin.php (legacy footer hook removed).
     
    155155== Upgrade Notice ==
    156156
    157 = 1.0.46 =
     157= 1.0.50 =
    158158Recommended update for WP.org submission compliance and stability fixes.
    159159
Note: See TracChangeset for help on using the changeset viewer.