<tr> in the DOM as the copy source for new rows. When the page loaded, our JavaScript ran initField() on that template and set data-aeci-init="1" on its wrapper element. Since ACF clones the template byte-for-byte — including data-* attributes — every new row inherited that flag from the moment it was created. The lazy-init guard then fired immediately on click (aeciInit === '1' → skip), the row was never registered in the fields map, and the modal never opened.
Fixed with two complementary changes: (1) initField() now skips any wrapper whose hidden input carries the disabled attribute — the reliable signal that an element is an ACF clone template rather than a real row; (2) getOrInitState() detects the “marked as initialised but not in map” inconsistency, resets the stale flag, and re-initialises the wrapper correctly. A delegated document-level click handler (also introduced in this release) then opens the modal on the very first click, with no page-save required.<i class="\u2026"> markup but the matching font stylesheet was never enqueued, so the icon appeared invisible. This affected every site upgrading to 2.0.0 / 2.0.1 that relied on custom icon sets.get_style_depends() mechanism, but only Font Awesome was declared as a dependency. Custom set stylesheets were registered under separate handles (aeci-icon-font-<slug>) and never pulled into the page..pot file available for any other language.••••••••XXXX) with Show key / Replace key buttons to prevent leaks via screenshots and screen-shares. The full key is auto-hidden again 10 seconds after revealing it./ focuses search, arrow keys navigate, Enter selects, Esc closes.aeci_license_*) no longer use autoload, reducing the size of the WordPress autoloaded-options query on every request. A one-shot migration converts existing options on first 2.0 page-load.wp-content/uploads/elementor/custom-icons/ is added or removed, instead of on every attachment change.manage_options capability after the nonce check (defense in depth)./recent-icons endpoint validates both value and library parameters with a strict regex and returns proper WP_REST_Response objects.$wpdb->esc_like() and $wpdb->prepare() consistently.Update URI: false to prevent accidental name collisions with WordPress.org plugins during automatic updates.error_log() calls that were left in production from development.proxy-v2.php debug code path; the production proxy lives server-side and HMAC v2 is the default transport.LIBXML_NONET and entity loader disabled — XXE-safe). Removes <script>, <foreignObject>, on* event handlers, javascript: / data: / vbscript: URLs, external <use href> references, DOCTYPE declarations and processing instructions. Sanitization runs at both upload and read time as a defense-in-depth measure.is_uploaded_file(), sanitize_file_name(), .svg extension check and a 250 KB hard size limit.uninstall.php cleans up options, transients and user_meta when the plugin is deleted. Set the AECI_KEEP_DATA_ON_UNINSTALL constant to true in wp-config.php to opt out.ACF Custom Icon, ACF Flexible/Repeater Custom Icon, ACF Taxonomy Term Custom Icon) renamed in panel to include “(Legacy)” in their titles (no change to slugs or internal logic).get_sub_field() first (for loop contexts), then falls back to get_field() if no sub-field exists, ensuring broader compatibility with flexible/repeater setups.http://, https://) and www., preventing accidental deactivations on minor SSL or subdomain changes.view setting in the Custom Icon widget to avoid PHP notices when the setting is undefined.WP_Term object directly to get_field() caused a critical error. The plugin now properly retrieves ACF field values for terms by using 'term_{term_id}' as the reference, ensuring stable integration with Elementor, JetEngine, and Elementor Pro Loop Grid.get_field(), get_sub_field()) to ensure they are correctly called from the global namespace, preventing fatal errors if ACF is active.