=== Oli AI Chatbot ===
Contributors: bigrat95
Donate link: https://www.paypal.com/donate/?hosted_button_id=29SAPKFAHMTNW
Tags: chatbot, search, faq, woocommerce, multilingual
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.1.12
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

On-site chatbot that answers from a local index of your pages, posts and products. Optional AI is off by default and uses only your own API key.

== Description ==

**Oli AI Chatbot** is a lightweight on-site chat for WordPress. By default it is **100 % local**: it indexes your published pages, posts, public custom post types and WooCommerce products, then answers with the best matching passages and their links. There is no account, no proxy and no service run by the plugin author.

Support: oli@olivierbigras.com  
Plugin links: https://github.com/olivierbigras

Optional AI is **off by default**. If you turn it on, you enter your own API key (OpenAI, Mistral, or a custom OpenAI-compatible base URL) on your site. The key is stored encrypted. Nothing is sent anywhere until you enable AI explicitly. See **External services**.

= Local answers =

* FULLTEXT index of passages (about 120–200 words, split on H2/H3 headings)
* Automatic fallback to LIKE + BM25 when FULLTEXT is unavailable
* Small-talk replies (hi / bonjour, thanks / merci, goodbye, who are you, help) before search — editable per language under Settings → Basic replies
* Friendlier no-result state with suggestion chips (FAQ / support, dealers, shop, contact) plus the contact form
* Light FR/EN stemming, editable synonyms, and typo tolerance against the terms table; relevance threshold is a setting (`oliaichat_min_score` filter)
* Each FAQ question/answer (accordions, Yoast/Rank Math blocks, FAQPage JSON-LD) is indexed on its own; definition questions show the answer first, product names and buy/price queries still rank products first
* Reindex on save and delete, plus WP-Cron batches of 25
* Language filter (Polylang, WPML, TranslatePress, or the site locale)
* WooCommerce products: title, short and long description, SKU, price, attributes
* Appearance tab: live preview, theme colours, launcher icons or a media image, desktop/mobile offsets, FR/EN copy, and show/hide rules

= Optional AI (off by default) =

* Provider select: OpenAI, Mistral, or a custom OpenAI-compatible URL
* Encrypted API key, never shown again after save
* Caps: 10 AI replies per session and 200 per day for the whole site (defaults)
* Test button on Settings → AI answers (admin only). It sends nothing until AI is enabled and a readable key is saved

= Conversations and email =

* A launcher opens an accessible chat panel; JavaScript and CSS load on hover, focus, click or idle
* The widget stores only sessionStorage (no cookies)
* Conversations are listed in the admin
* Transcripts are emailed (HTML + plain text) to the site owner when a visitor ends the chat (if the chat had messages) and after 15 minutes of inactivity. The visitor can optionally enter their email on End to receive the same transcript.
* “Send my question by email” appears when local search finds nothing
* Log retention defaults to 30 days (0 = do not store message bodies; open session rows are kept until the chat ends)

= Multilingual =

Works with Polylang, WPML and TranslatePress (and a single-language site). Interface strings are in English; community translations come from translate.wordpress.org.

== Privacy ==

* **Anonymized questions and votes.** Questions asked in the chat may be stored in a learning log after emails, phone numbers and obvious names are stripped. Only the question text, language, outcome (answered / unanswered / emailed), an optional “Was this helpful?” vote, a count and the last date are kept. No IP, email or page URL is stored in that log.
* **30-day purge by default.** Raw questions, votes and conversation message bodies follow Settings → Privacy retention (default 30 days). Set 0 to store no logs. Open session rows are kept until the chat ends.
* **Learned FAQs are kept.** Answers the site owner writes under Settings → Answers are not purged with the question log.
* **Transcripts by email to the admin.** When a visitor ends a chat that had at least one message, or after 15 minutes of inactivity, a transcript is emailed to the site owner. Opening the launcher without chatting emails nobody.
* **Optional visitor email.** On End, the visitor may enter an address to receive the same transcript. That address is used only for that copy. The contact form emails the owner with Reply-To set to the visitor.
* **Front widget.** sessionStorage only; no cookies, no localStorage, no tracker. Treat the script as functional in Complianz (no tracking cookies).
* **Rate limits.** Public REST limits use a hashed IP in a WordPress transient. The learning log still stores no IP.
* WordPress export and erase tools cover conversations tied to an email from the contact form or the optional End transcript field.
* Keep-data option on uninstall (off by default). Suggested privacy-policy text is added under Settings → Privacy.

== Installation ==

1. Upload the `oli-ai-chatbot` folder to `/wp-content/plugins/`, or install the zip from **Plugins > Add New > Upload Plugin**
2. Activate the plugin through the **Plugins** menu in WordPress
3. Go to **Settings > Oli AI Chatbot > Index**, choose the content types, and click **Reindex site**
4. Use **Try a local search** on that same tab to confirm passages are found
5. Review General, Privacy and (optionally) AI answers

== Frequently Asked Questions ==

= Does the plugin call the internet by default? =

No. With AI disabled (the default), there are no external HTTP requests. Indexing blocks outbound HTTP while rendering content.

= Where is the API key stored? =

On your site, encrypted with sodium (`crypto_secretbox`) using a key derived from your WordPress salts. If the salts change, the plugin asks you to enter the key again. The key is never sent to the plugin author.

= Which AI providers are supported? =

OpenAI, Mistral, or a custom OpenAI-compatible base URL. AI stays off until you enable it and save your own key. See **External services** for what is sent and when.

= Can I use my own OpenAI-compatible server? =

Yes. Choose **Custom OpenAI-compatible base URL**, enter the base URL and key, enable AI answers, then use **Test key**. The request goes only to the URL you entered.

= How long is data kept? =

30 days by default. Change it under Settings → Privacy (0 = do not store question or conversation logs). Learned FAQ answers are kept.

= Are conversations visible in the admin? =

Yes. Settings > Oli AI Chatbot > Conversations lists stored chats. Ending a chat emails the owner a transcript only if the visitor sent at least one message. The End step offers an optional email field: if the visitor enters a valid address they also receive a copy (same templates, their language, no owner-only system lines), rate limited. An empty email keeps the button as “End conversation” and only the owner is copied. Opening the launcher without chatting emails nobody. The contact form emails the site owner with Reply-To set to the visitor.

= Does it work without WooCommerce? =

Yes. Pages and posts are enough. When WooCommerce is active, products can be indexed (title, short and long description, SKU, price, attributes).

= What happens on uninstall? =

Tables, options, the dedicated capability, crons and transients are removed, unless **Keep the data when the plugin is deleted** is checked.

= How do I get support? =

Email oli@olivierbigras.com.

== External services ==

Oli AI Chatbot does **not** call olivierbigras.com, Google, Cloudflare Turnstile, maps, spreadsheets or webfonts. There is no proxy, no telemetry and no account with the plugin author. Outbound HTTP happens only when a site administrator enables **AI answers** (opt-in) or clicks a **Donate** link (opt-in).

When AI is enabled, visitor questions and the matching local passages are sent to the selected provider so it can draft an answer. Typical data: the question text, retrieved passage excerpts, the configured model name, and the site language. The Test key button sends only a short ping (“pong”) from the admin screen — no visitor text. No WordPress passwords or API keys of other plugins are sent.

= OpenAI =

* Used when the provider is set to OpenAI (or a custom URL that points to OpenAI).
* When: after the site owner enables AI answers and saves their own OpenAI API key; also when they click Test key.
* Endpoint: https://api.openai.com/v1/chat/completions
* Data sent: the question and retrieved passages (or “pong” for Test key), plus the configured model name and site language.
* Terms of use: https://openai.com/policies/terms-of-use
* Privacy policy: https://openai.com/policies/privacy-policy

= Mistral =

* Used when the provider is set to Mistral.
* When: after the site owner enables AI answers and saves their own Mistral API key; also when they click Test key.
* Endpoint: https://api.mistral.ai/v1/chat/completions
* Data sent: the question and retrieved passages (or “pong” for Test key), plus the configured model name and site language.
* Terms of service: https://mistral.ai/terms/
* Privacy policy: https://mistral.ai/terms/#privacy-policy

= Custom OpenAI-compatible endpoint =

* Used when the provider is set to a custom base URL.
* When: after the site owner enables AI answers, saves their key, and (optionally) clicks Test key.
* Data sent: the same payload as above, to the URL the site owner entered.
* Terms and privacy: those of the service behind that URL (the site owner is responsible for reviewing them).

= PayPal (Donate) =

* Used only when a site administrator clicks Donate / Buy me a coffee in the plugin list or on Settings → Oli AI Chatbot. The plugin never contacts PayPal in the background.
* Endpoint: https://www.paypal.com/donate/?hosted_button_id=29SAPKFAHMTNW
* Data sent: whatever PayPal collects on that hosted button page after the click (the plugin itself sends nothing).
* Terms: https://www.paypal.com/us/legalhub/paypal/useragreement-full
* Privacy: https://www.paypal.com/us/legalhub/privacy-full

The plugin author site (https://olivierbigras.com) is listed as Author URI and in a local staging mail-guard rewrite. The plugin does not request that site over HTTP.

The **Test key** button is the only admin-triggered outbound AI call besides live answers. Both require AI answers to be enabled; otherwise the plugin reports that nothing was sent.

== Screenshots ==

1. Settings → Oli AI Chatbot (General)
2. Index tab with reindex progress and local search
3. AI answers tab (disabled by default) with the Test key button
4. Front chat showing local passage cards
5. Appearance tab with a live preview of the widget

== Changelog ==

= 1.1.12 =
* Unique `oliaichat_` / `OLIAICHAT_` / `Oliaichat_` prefix for functions, classes, constants, options, transients, user meta, cron hooks, AJAX actions, nonces, filters, CSS classes and JS globals; one-time, idempotent migration from `oliaic_*` (options, transients, user meta, capability, cron events and tables), old names removed afterwards
* Admin screen: every link carries a nonce; tab, conversation, Answers prefill (`gap`, `q`, `lang`) and notices are read only with the plugin capability and a valid nonce; the settings tab field is trusted only with the Settings API nonce
* Answers form handled on `load-{page}` before output (nonce + capability), legacy settings slugs redirect from hidden pages
* Late escaping: SVG icons and launcher markup go through `wp_kses()` with an explicit SVG allow-list; inline CSS built only from validated values and escaped at output; inline JS data uses `wp_json_encode()` with `JSON_HEX_*` flags (custom encoder removed)
* SQL: no more `call_user_func_array()` around `$wpdb->prepare()`; literal queries with `%i` identifiers and generated `%s` placeholder lists
* TranslatePress language switch uses `trp_switch_language()` / `trp_restore_language()`; `$TRP_LANGUAGE` is only read through `$GLOBALS`; core `$l10n_unloaded` is no longer touched (`unload_textdomain( ..., true )`)

= 1.1.11 =
* Display name is **Oli AI Chatbot**; slug / text domain is `oli-ai-chatbot`
* PHP identifiers, options, tables, hooks, REST namespace, and script handles use the `oliaic_` / `OLIAIC_` prefix; one-time migration from `oliwpc_*`, `oli_chatbot_*` and `oli_cb_*`
* Activation loads `wp-admin/includes/upgrade.php` before `dbDelta()`; tables use `$wpdb->prefix` and `get_charset_collate()`
* Bundled `.po` / `.mo` / `.l10n.php` files and `load_plugin_textdomain()` removed (Requires at least 6.4; translations via translate.wordpress.org)
* Admin notices stay on the plugin / Plugins screens and are dismissible; admin CSS is scoped to `.oliaic-wrap`
* External services (OpenAI, Mistral, optional custom URL, PayPal Donate) remain documented; no outbound HTTP on activation
* Cron schedule label is not translated at `plugins_loaded` (avoids `_load_textdomain_just_in_time`)
* Staging mail guard rewrites to `admin_email` (`oliaic_mail_guard_recipient` filter); no author email in runtime code
* Author URI is https://github.com/olivierbigras (Plugin URI header removed)
* If the previous `oli-chatbot` plugin is still active: no front launcher or REST from this copy, no `oli_chatbot_settings` rewrite, one dismissible notice on this plugin’s settings screen only. The `oliaic_manage` capability is granted in standby (settings also accept `manage_options`)

= 1.1.10 =
* Front launcher waits for `#oliaic-root` (footer print order + deferred `boot.js` + `DOMContentLoaded`) so the chat button opens the panel
* `critical.css` and appearance tokens stay in `wp_enqueue_scripts` (`<head>`) so a theme without `wp_footer` still loads the launcher CSS

= 1.1.9 =
* Regenerated `languages/oli-ai-chatbot.pot` with `wp i18n make-pot` (PHP and JS, including `chat.js`); updated French `.po` / `.mo` / `.l10n.php`. JS JSON from `wp i18n make-json` (handles `oli-ai-chatbot-admin`, `oli-ai-chatbot-chat`)
* Admin appearance preview fallbacks and contrast labels use `wp.i18n.__()` (`oli-ai-chatbot`)
* `Domain Path: /languages`; `load_plugin_textdomain` on `init` only if WordPress is older than 6.7 (not during locale switches on 6.7+)
* `phpcs.xml.dist` excludes `/languages/*` and `/vendor/*`; `languages/` stays in the distribution ZIP
* Catalog includes the prefix-migration strings (503 migrating message, failed/conflict notices, Try again) and the translated network-error string used by `ask()`

= 1.1.8 =
* Regenerated the translation catalog with `wp i18n make-pot` (Plugin URI is https://olivierbigras.com)
* Updated French `.po` / `.mo` / `.l10n.php` and JS JSON for 1.1.7 strings; Project-Id-Version is 1.1.8
* File headers no longer include the wordpress.org contributor handle (Contributors slug unchanged)
* `.distignore` and `.gitattributes` export-ignore keep development files out of the ZIP
* Prefix migration runs first; the lock option is always deleted. A failed rename increments a database counter and retries in a bounded loop (3 attempts, no sleep). After 3 failures an incomplete-migration notice offers Retry (403 for non-admins). Chat stays on intact 1.1.5 tables or is disabled with a 503. Indexing/terms/cleanup freeze until `done` or `conflict`.
* Network errors in the chat widget use the translated “Couldn’t reach the server.” string (`ask()` catch no longer shows raw Failed to fetch)
* Settings fall back to `oli_cb_settings` when `oliaic_settings` is missing; the widget stays hidden when neither exists

= 1.1.7 =
* Versioned db_version routine renames or copies 1.1.5 `oli_cb_*` tables to `oliaic_*` before create_tables (idempotent, no data loss, runs on plugins_loaded)
* Activation no longer resets settings: add_option plus merge of missing default keys only
* Front critical CSS and boot loader are enqueued files; appearance tokens use wp_add_inline_style (no element style= / no raw script tags)
* External services documents PayPal Donate (opt-in) and states that olivierbigras.com is not called
* Plugin URI points to https://olivierbigras.com; brand names removed from copy and default skip-tokens

= 1.1.6 =
* Front critical CSS and boot config/loader use wp_enqueue_style/script with inline data (no raw `<style>` or `<script>` tags)
* Configuration JSON is encoded with hex flags to block script injection from settings
* PHP identifiers, options, hooks, script handles, and database tables use the `oliaic_` / `OLIAIC_` prefix; one-time migration from 1.1.5 keys
* Admin notices are limited to relevant screens, dismissible per user, and non-persistent site-wide

= 1.1.5 =
* No-result chips are deduped by intent and target URL, so a Dealers page no longer also shows the built-in “Find a dealer”
* Chips taken from page titles use the TranslatePress title/URL for the current language, or the localized built-in label when the title is still English

= 1.1.4 =
* Search results include post_type again; product-only detection requires post_type=product so informational page hits are logged as answered
* Warranty and dealer questions require the content token (warranty, dealer/revendeur) — brand words are optional — and boost a matching page title (1.0.4 Online Warranty baseline)
* A downvoted answered question shows “Negative vote” in To learn, not Unanswered
* No-result chips are deduped and filled to 3–4 distinct localized suggestions (FAQ/support, dealers, shop, contact)

= 1.1.3 =
* Buy/model queries detect the model name from product titles and give exact model-in-title a large boost
* Accessory titles (bag, case, box, bolt, tuner, jack, strap, cover) and parts/accessories/cases categories are demoted unless the question names that accessory
* Instrument products are recognized from guitar-family categories (acoustic-electric, electrics, nylon) without requiring the word “guitar”

= 1.1.2 =
* Definition search drops stopwords and intent words (quoi, c’est, what, is) before scoring; no fuzzy match on tokens under 5 characters; the key phrase must appear or the bot shows no-result
* Buy/model queries boost exact title matches and main products (guitars) over accessories and spare parts
* “Link a page” FAQ answers use the first ~300 characters of cleaned page content, plus the page link
* On a short mobile sheet (cookie banner), the header is compact; under ~260px of room the panel overlays the banner and shows a hint
* Session/ask rate limits are 20 per minute per session, with a friendly “try again” message

= 1.1.1 =
* French definition questions (c’est quoi, qu’est-ce que, comment, pourquoi, à quoi sert) are informational even when a product title repeats an English loanword
* Cart, checkout and account pages (WooCommerce IDs and slugs, including /fr/cart) are excluded from the index
* Informational queries require a topic / phrase match (truss rod stays a phrase across languages); weak or unrelated chunks become the no-result state
* Definition questions never rank products first: FAQ, pages and posts only, otherwise no-result and the question is logged unanswered in To learn
* Weak or product-only informational answers are stored as unanswered so they appear under Answers → To learn
* On mobile, the open chat panel sits in the space above a bottom cookie banner (`--ocb-banner-offset`) and resizes when the banner appears or is dismissed
* Local FAQ cards link to the page chosen under “Or link a page”; answers without a linked page have no broken empty link

= 1.1.0 =
* Smarter local mode (no AI): greetings and other short small-talk no longer show “nothing found”
* New Settings tab “Basic replies” / Réponses de base: editable FR + EN replies for greet, thanks, bye, who are you, help, and no-result, using the site name
* Greeting and help replies include suggestion chips; no-result uses a friendly rephrase, 3–4 chips, and the existing contact-form button
* Suggestion chips default to shop/products, shipping/delivery, contact and FAQ pages when those pages exist (or a custom list)
* More tolerant search: light FR/EN stemming, editable synonym groups, Levenshtein typo correction on indexed terms (length ≥ 5)
* Relevance threshold is now a setting (default 0.28) and still filterable via `oliaic_min_score`
* Terms-table lookup is cached (transient + request memory) and invalidated when the index changes
* Definition questions (what is, c’est quoi, comment…) boost FAQ / pages and demote products; products win on buy/price/stock or a product title match
* Each FAQ question/answer is indexed as its own `faq` chunk (details/summary, Elementor/Kadence/Gutenberg accordions, Yoast and Rank Math FAQ blocks, FAQPage JSON-LD) with a title-match bonus and the answer shown as a card plus page anchor
* Full Appearance tab with a live preview: accent/text/background/bubble colours (wp-color-picker), “Use theme colors”, six launcher icons or a media image, desktop/mobile position and offsets, size, radius, font, dark mode, FR/EN bot name, avatar, welcome and launcher text, plus show/hide rules (IDs, post types, URL patterns) with a contrast warning under 4.5:1
* Answers tab: anonymized log of each question (answered / unanswered / emailed) with counts and last date; “Was this helpful?” votes nudge local ranking; To learn inbox to write an answer or link a page (local FAQ table, FR/EN, auto variants, top priority). After a contact/transcript email, save the reply as a FAQ draft to validate (Conversations → Save as knowledge). Optional AI draft uses only the site owner’s key. Cookie-banner offset now drops when the visitor accepts (Complianz events + observers)

= 1.0.5 =
* Opening or closing the launcher no longer POSTs /session; a session is created on the first ask or contact submit
* Contact and End email fields accept dotted local parts (`qa.test@example.com`); the server still uses `is_email()`
* Mobile launcher (390px) lifts above a bottom cookie banner the same way as on desktop

= 1.0.4 =
* Desktop panel height is capped to the viewport (including when the launcher sits above a cookie banner) so the header stays on screen when the End email field is open; the message list scrolls inside
* Passage titles and excerpts decode HTML entities (`&amp;`, `&#039;`) at index time and render as text
* Saving excluded IDs/types, or starting a reindex, now removes leftover chunks and terms for posts that are no longer indexed

= 1.0.3 =
* Opening the launcher no longer creates a conversation row; the row is created on the first visitor message
* Transcripts are never emailed for conversations with zero visitor messages (End and the 15-minute idle cron)
* End step: optional visitor email for a transcript copy; live label “End conversation” / “End and receive transcript”; no mail (owner or visitor) when there were no visitor messages
* Contact form error summary names the invalid field when only one field fails
* Dark-mode card title links meet 4.5:1 contrast against the panel
* Excluded pages (post IDs) plus automatic exclusion of Complianz cookie/policy pages, Complianz popup types, and non-public post types
* Search results are deduplicated by URL and near-identical titles
* Higher relevance threshold (`oliaic_min_score`, default 0.4) so weak matches show the contact form
* Desktop launcher sits above a bottom cookie banner (`--ocb-banner-offset`) without raising z-index over dialogs

= 1.0.2 =
* Final public chat UI (markup, ARIA, keyboard, light-dark fallback, forced-colors)
* Card excerpts trimmed to about 120 characters on the server
* Contact name is optional; consent checkbox is required
* Default accent #2F5BD3 with automatic ink contrast
* wordpress.org icons and banners replaced with delivered artwork
* Bundled fr_CA / fr_FR translations ship in the zip (load_plugin_textdomain fallback; translate.wordpress.org packs still win)

= 1.0.1 =
* TranslatePress: index each language (dictionary / render) with /fr/ URLs and detect /fr/wp-json/ REST language
* Default indexed types include WooCommerce products when Woo is active
* Chunk URLs use the home option scheme (https under WP-CLI)
* Front strings and contact/transcript emails follow the visitor language (switch_to_locale + plugin .mo)

= 1.0.0 =
* Initial public skeleton (M0) and local index (M1)
* Database tables for passages, conversations, messages and term document-frequency
* Settings page with General, Index, AI, Emails, Privacy, Appearance and Conversations tabs
* Chunker, indexer, WP-Cron queue (batches of 25), save/delete hooks
* Local FULLTEXT search with LIKE + BM25 fallback, corpus IDF and language edge cases
* Public REST: session, ask, end, contact (hashed tokens, nonces, honeypot, rate limits)
* Front chat launcher with lazy assets, sessionStorage and keyboard / screen-reader support
* HTML transcripts, 15-minute idle mail, contact form, staging mail guard, privacy export/erase
* Encrypted storage for the optional AI key
* Optional client-side AI (OpenAI, Mistral, custom OpenAI-compatible URL), RAG over local passages, caps 10/session and 200/day
* Admin Test key button; zero outbound HTTP until AI is enabled
* French (Canada) and French (France) translations (.pot, .po, .mo, .l10n.php) plus JS JSON via wp_set_script_translations
* PHPCS (WPCS 3.1) and PHPStan level 6 tooling
* PHPUnit 9 suite, Playwright e2e (launcher / chat / contact / end), Lighthouse CLS check, Plugin Check (zero errors)
* Release zip via tools/build-zip.sh; .wordpress-org icon/banner placeholders and test-site screenshots (not submitted)

== Upgrade Notice ==

= 1.1.12 =
WordPress.org review fixes: new `oliaichat_` prefix (settings, data and tables are migrated once), nonces on all admin requests, late escaping and prepared SQL. No reindex required.

= 1.1.11 =
Renames the plugin to Oli AI Chatbot (`oli-ai-chatbot`). Existing settings, conversations, and index tables are migrated once. No reindex required. Re-save Settings if you bookmarked the old options URL.

= 1.1.10 =
Fixes the front chat button doing nothing when boot.js ran before #oliaic-root. No database change.

= 1.1.9 =
Translation catalog refresh (PHP + JS, make-json from .po), Domain Path header, and textdomain load only on WordPress older than 6.7. No database change.

= 1.1.8 =
Translation catalog and French packs refreshed. Distribution ZIP excludes development files. No database change.

= 1.1.7 =
Fixes 1.1.5 table rename and settings reset on reactivation. Review Settings → AI answers if you use a provider key (never committed; stored encrypted on your site).

= 1.1.5 =
No-result chips no longer repeat Dealers / Find a dealer, and French chips use the translated page title (or the localized built-in). No reindex.

= 1.1.4 =
Warranty and dealer questions rank the matching support page first again (as in 1.0.4). Downvoted answers show as a negative vote. Reindex is optional.

= 1.1.3 =
Named-model searches rank instruments above gig bags, cases and parts. Reindex so product tags and categories are stored for ranking.

= 1.1.2 =
Local search is stricter on definition questions and ranks named models above spare parts. Reindex if you want product categories in the index.

= 1.1.1 =
Reindex so cart, checkout and account pages leave the index. Definition questions no longer return product-only cards.

= 1.1.0 =
Local search understands short greetings and is more tolerant of typos. Review Settings → Answers to teach the bot missed questions, and Appearance for the widget. Reindex so FAQ accordions become answer cards.

= 1.0.5 =
Opening the chat no longer writes a session. Valid emails with dots in the local part are accepted.

= 1.0.4 =
Reindex after update so titles stored with HTML entities are decoded and leftover excluded chunks are dropped.

= 1.0.3 =
Empty launcher opens no longer email a blank transcript. Re-save Index if you want Complianz pages dropped from an existing index.

= 1.0.2 =
Refreshed chat panel. Re-save Appearance if you want the new default accent #2F5BD3.

= 1.0.1 =
Reindex after update on TranslatePress or WooCommerce sites so French chunks and products are stored.

= 1.0.0 =
Initial release of the local chatbot index and settings.
