diff --git a/changai/changai/api/v2/assets/stop_words.json b/changai/changai/api/v2/assets/stop_words.json new file mode 100644 index 0000000..ee58fc1 --- /dev/null +++ b/changai/changai/api/v2/assets/stop_words.json @@ -0,0 +1,186 @@ +[ + "hi", + "hello", + "hey", + "thanks", + "thank", + "please", + "pls", + "ok", + "okay", + "yes", + "no", + "bye", + "goodbye", + "have", + "has", + "had", + "do", + "does", + "did", + "what", + "which", + "who", + "whom", + "whose", + "where", + "when", + "why", + "how", + "can", + "could", + "would", + "should", + "do", + "does", + "did", + "is", + "are", + "was", + "were", + "be", + "been", + "being", + "the", + "a", + "an", + "to", + "for", + "from", + "of", + "in", + "on", + "at", + "by", + "with", + "without", + "and", + "or", + "but", + "if", + "then", + "than", + "as", + "this", + "that", + "these", + "those", + "it", + "its", + "there", + "here", + "show", + "list", + "give", + "get", + "find", + "display", + "tell", + "me", + "need", + "want", + "make", + "create", + "check", + "see", + "view", + "today", + "yesterday", + "tomorrow", + "now", + "current", + "latest", + "last", + "next", + "this", + "week", + "month", + "year", + "daily", + "weekly", + "monthly", + "yearly", + "مرحبا", + "مرحبًا", + "اهلا", + "أهلا", + "أهلًا", + "السلام", + "شكرا", + "شكرًا", + "نعم", + "لا", + "طيب", + "تمام", + "مع السلامة", + "ما", + "ماذا", + "من", + "متى", + "أين", + "اين", + "كيف", + "لماذا", + "هل", + "كم", + "أي", + "اي", + "الذي", + "التي", + "الذين", + "في", + "من", + "إلى", + "الى", + "على", + "عن", + "مع", + "بدون", + "و", + "أو", + "او", + "لكن", + "إذا", + "اذا", + "ثم", + "هذا", + "هذه", + "هؤلاء", + "ذلك", + "تلك", + "هنا", + "اعرض", + "عرض", + "اظهر", + "أظهر", + "هات", + "اعطني", + "أعطني", + "اريد", + "أريد", + "احتاج", + "ابحث", + "تحقق", + "شوف", + "اليوم", + "أمس", + "امس", + "غدا", + "غدًا", + "الآن", + "الان", + "الحالي", + "الأخير", + "الاخير", + "هذا", + "هذه", + "الأسبوع", + "الاسبوع", + "الشهر", + "السنة", + "العام", + "يومي", + "أسبوعي", + "شهري", + "سنوي" +] \ No newline at end of file diff --git a/changai/changai/api/v2/assets/thread_words.json b/changai/changai/api/v2/assets/thread_words.json new file mode 100644 index 0000000..e89bea6 --- /dev/null +++ b/changai/changai/api/v2/assets/thread_words.json @@ -0,0 +1,122 @@ +[ + "yes", + "yep", + "yeah", + "yup", + "yes please", + "list", + "of course", + "sure", + "surely", + "absolutely", + "definitely", + "certainly", + "indeed", + "correct", + "ofcourse", + "right", + "exactly", + "precisely", + "ok", + "okay", + "fine", + "alright", + "go ahead", + "do it", + "show me", + "please", + "go on", + "continue", + "proceed", + "why not", + "aye", + "affirmative", + "true", + "agreed", + "hmm", + "hm", + "umm", + "uh", + "ah", + "give", + "interesting", + "i see", + "got it", + "ok got it", + "and", + "so", + "then", + "also", + "but", + "what", + "how", + "when", + "who", + "where", + "why", + "more", + "less", + "again", + "another", + "other", + "next", + "previous", + "back", + "forward", + "noted", + "understood", + "makes sense", + "okay okay", + "fine fine", + "sure sure", + "show", + "نعم", + "أجل", + "بالتأكيد", + "طبعاً", + "حسناً", + "موافق", + "صحيح", + "بالضبط", + "تماماً", + "إي", + "ماشي", + "تمام", + "أوكي", + "يلا", + "استمر", + "كمّل", + "واضح", + "فاهم", + "مفهوم", + "اوك", + "و", + "ثم", + "لكن", + "أيضاً", + "كذلك", + "ماذا", + "كيف", + "متى", + "من", + "أين", + "لماذا", + "أكثر", + "أقل", + "مرة أخرى", + "التالي", + "السابق", + "حسناً حسناً", + "تمام تمام", + "مزيد", + "غيره", + "لا", + "لأ", + "لا شكراً", + "إلغاء", + "توقف", + "اتركه", + "مش محتاج", + "مو صح", + "خطأ" +] \ No newline at end of file diff --git a/changai/changai/api/v2/auto_gen_api.py b/changai/changai/api/v2/auto_gen_api.py index e939eef..5767a25 100644 --- a/changai/changai/api/v2/auto_gen_api.py +++ b/changai/changai/api/v2/auto_gen_api.py @@ -33,13 +33,10 @@ def ensure_file_folder(folder_path: str, is_private: int = 1) -> str: """ if not folder_path: return "Home" - parts = [p.strip() for p in folder_path.split("/") if p.strip()] if not parts: return "Home" - current_path = parts[0] - # Usually Home already exists, but keep this safe. if not frappe.db.exists("File", current_path): frappe.get_doc({ @@ -49,7 +46,6 @@ def ensure_file_folder(folder_path: str, is_private: int = 1) -> str: "folder": "", "is_private": is_private, }).insert(ignore_permissions=True) - for part in parts[1:]: next_path = f"{current_path}/{part}" if not frappe.db.exists("File", next_path): @@ -61,7 +57,6 @@ def ensure_file_folder(folder_path: str, is_private: int = 1) -> str: "is_private": is_private, }).insert(ignore_permissions=True) current_path = next_path - return current_path @@ -121,22 +116,18 @@ def write_filedoctype( is_private: int = 1 ): folder = ensure_file_folder(folder, is_private=is_private) - if file_name.endswith(JSON_EXT): text = json.dumps(payload, ensure_ascii=False, indent=2) elif file_name.endswith((YAML_EXT, ".yml")): text = yaml.safe_dump(payload, allow_unicode=True, sort_keys=False) else: text = str(payload) - content = text.encode("utf-8") - existing = frappe.db.get_value( "File", {"file_name": file_name, "folder": folder}, "name" ) - if existing: doc = frappe.get_doc("File", existing) frappe.logger().info(f"Overwriting {file_name} -> file_url={doc.file_url}") @@ -144,7 +135,6 @@ def write_filedoctype( doc.save(ignore_permissions=True) doc.reload() return doc - doc = frappe.get_doc({ "doctype": "File", "file_name": file_name, @@ -152,7 +142,6 @@ def write_filedoctype( "is_private": is_private, "content": content, }).insert(ignore_permissions=True) - return doc def _tab(dt: str) -> str: dt = (dt or "").strip() @@ -169,32 +158,24 @@ def _strip_tab(t: str) -> str: def _normalize_master_data_payload(payload: Any) -> tuple[Dict[str, Any], List[Dict[str, Any]]]: if not isinstance(payload, dict): payload = {} - meta = payload.get("_meta") or {} data = payload.get("data") or [] - if not isinstance(meta, dict): meta = {} - if not isinstance(data, list): data = [] - return meta, data def _extract_existing_keys(data: List[Any]) -> Set[tuple]: keys: Set[tuple] = set() - for row in data: if not isinstance(row, dict): continue - dt = row.get("entity_type") eid = row.get("entity_id") - if dt and eid: keys.add((dt, eid)) - return keys @@ -233,20 +214,15 @@ def update_masterdata(): def sync_master_data_smart() -> Dict[str, Any]: file_name = "master_data.yaml" payload = _read_filedoctype(file_name, RAG_FOLDER) - meta, data = _normalize_master_data_payload(payload) - added_total = 0 removed_total = 0 added_by_module: Dict[str, int] = {} removed_by_module: Dict[str, int] = {} fetched_by_module: Dict[str, int] = {} - rebuilt_rows: List[Dict[str, Any]] = [] - for mod in MODULES_TO_SYNC: entity_type = f"tab{mod}" - existing_rows = [ row for row in data if isinstance(row, dict) and row.get("entity_type") == entity_type @@ -263,18 +239,13 @@ def sync_master_data_smart() -> Dict[str, Any]: fields.append(title_field) live_records = frappe.get_all(mod, fields=fields,limit_page_length=0) live_ids = {rec.get("name") for rec in live_records if rec.get("name")} - fetched_by_module[mod] = len(live_ids) - added_ids = live_ids - existing_ids removed_ids = existing_ids - live_ids - added_by_module[mod] = len(added_ids) removed_by_module[mod] = len(removed_ids) - added_total += len(added_ids) removed_total += len(removed_ids) - for rec in live_records: if mod == "Item": item_code = rec.get("name") @@ -297,9 +268,7 @@ def sync_master_data_smart() -> Dict[str, Any]: else: entity_id = rec.get(title_field) if title_field in rec else rec.get("name") rebuilt_rows.append(_build_master_data_row(entity_type, entity_id,title_field)) - final_data = rebuilt_rows - meta["last_sync"] = str(now_datetime()) settings = frappe.get_single("ChangAI Settings") settings.last_masterdata_sync = meta["last_sync"] @@ -325,15 +294,12 @@ def _clean_schema_fields(by_table: Dict[str, Dict[str, Any]]) -> None: for field in block.get("fields", []) or []: if not isinstance(field, dict): continue - if field.get("fieldtype") != "Select": field.pop("options", None) - if field.get("fieldtype") != "Link": field.pop("join_hint", None) - def get_doctypes_changed_since(last_sync: Optional[str]) -> List[str]: app_names=["erpnext","frappe"] erpnext_modules = get_mod(app_names) @@ -348,9 +314,7 @@ def get_doctypes_changed_since(last_sync: Optional[str]) -> List[str]: filters["modified"] = [">=", since] # catches updated tables except Exception: pass - results = frappe.get_all("DocType", filters=filters, pluck="name") - # Also catch newly created DocTypes since last sync if last_sync: try: @@ -368,25 +332,20 @@ def get_doctypes_changed_since(last_sync: Optional[str]) -> List[str]: results = list(set(results) | set(new_doctypes)) except Exception: pass - return results TABLES_JSON = "tables.json" YML_EXTENSIONS = (".yaml", ".yml") REPORTS_JSON = "reports.json" - def _normalize_schema_payload(payload: Any) -> tuple[Dict[str, Any], List[Dict[str, Any]]]: if not isinstance(payload, dict): return {}, [] - meta = payload.get("_meta") or {} tables_blocks = payload.get("tables") or [] - if not isinstance(meta, dict): meta = {} if not isinstance(tables_blocks, list): tables_blocks = [] - return meta, tables_blocks @@ -415,16 +374,13 @@ def _get_tables_to_process( ) -> tuple[Set[str], Set[str], Set[str], List[str], List[str]]: changed_tables = {_tab(dt) for dt in changed_doctypes} existing_tables_set = set(existing_tables) - missing_from_schema = {t for t in existing_tables if t not in by_table} new_from_changed = { t for t in changed_tables if t not in by_table and t not in existing_tables_set } - tables_to_process = sorted(changed_tables | missing_from_schema | new_from_changed) merged_tables = sorted(existing_tables_set | changed_tables) - return changed_tables, missing_from_schema, new_from_changed, tables_to_process, merged_tables @@ -439,12 +395,10 @@ def _get_existing_fields_for_table(by_table: Dict[str, Dict[str, Any]], table: s def _merge_select_options(live_options_raw: str, existing_options: Any) -> List[str]: live_options = [opt.strip() for opt in live_options_raw.split("\n") if opt.strip()] - if isinstance(existing_options, str): existing_options = [opt.strip() for opt in existing_options.split("\n") if opt.strip()] elif not isinstance(existing_options, list): existing_options = [] - return list(dict.fromkeys(live_options + existing_options)) @@ -454,33 +408,19 @@ def _build_fields_from_meta( ) -> List[Dict[str, Any]]: fields: List[Dict[str, Any]] = [] added_fieldnames = set() - - # # always add system fields first - # for sys_field in SYSTEM_FIELDS: - # field_entry = _build_field_entry(sys_field, existing_fields, meta_dt.name) - # if field_entry: - # fields.append(field_entry) - # added_fieldnames.add(field_entry["name"]) - - # then add real doctype fields for field_meta in meta_dt.fields: fieldname = (getattr(field_meta, "fieldname", None) or "").strip() fieldtype = (getattr(field_meta, "fieldtype", None) or "").strip() - if not fieldname: continue - if fieldname in added_fieldnames: continue - if fieldtype in EXCLUDED_FIELDTYPES: continue - field_entry = _build_field_entry(field_meta, existing_fields, meta_dt.name) if field_entry: fields.append(field_entry) added_fieldnames.add(field_entry["name"]) - return fields @@ -493,7 +433,6 @@ def _update_or_create_table_block( by_table[table]["fields"] = fields by_table[table]["desc_done"] = not _has_pending_descriptions(fields) return - by_table[table] = { "table": table, "description": "", @@ -506,7 +445,6 @@ def _build_field_entry( existing_fields: Dict[str, Dict[str, Any]], source_doctype: str, ) -> Optional[Dict[str, Any]]: - if isinstance(field_meta, dict): fieldname = field_meta.get("fieldname") fieldtype = field_meta.get("fieldtype", "Data") @@ -517,38 +455,30 @@ def _build_field_entry( fieldtype = getattr(field_meta, "fieldtype", "Data") label = getattr(field_meta, "label", None) or fieldname options = getattr(field_meta, "options", None) - if not fieldname: return None - existing = existing_fields.get(fieldname) or {} description = existing.get("description") or "" - entry = { "name": fieldname, "fieldtype": fieldtype, "label": label, "description": description, } - if fieldtype == "Select" and options: entry["options"] = _merge_select_options( options, existing.get("options", []), ) - elif fieldtype == "Link" and options: entry["join_hint"] = { "table": f"tab{options}", "on": f"{fieldname} = tab{options}.name" } - if fieldtype != "Select": entry.pop("options", None) - if fieldtype != "Link": entry.pop("join_hint", None) - return entry @@ -578,7 +508,6 @@ def _write_schema_outputs( ) - def _has_pending_descriptions(fields: List[Dict[str, Any]]) -> bool: return any( not (field.get("description") or "").strip() @@ -911,24 +840,19 @@ def _process_pending_field_batches( updated_in_table = 0 updated_fields = 0 consecutive_errors = 0 - for i in range(0, len(pending_fields), batch_size): batch = pending_fields[i:i + batch_size] desc_map = _smart_desc_map(client, table, batch) - if not desc_map: consecutive_errors += 1 continue - consecutive_errors = 0 - for field in batch: field_name = field.get("name") if field_name in desc_map: field["description"] = desc_map[field_name].strip() updated_fields += 1 updated_in_table += 1 - # Required checkpoint commit: this long-running schema enrichment job calls external APIs and must persist partial progress to avoid losing completed batch updates on failure/retry. frappe.db.commit() # nosemgrep return { @@ -950,10 +874,8 @@ def _process_table_for_missing_descriptions( "consecutive_errors": 0, "skipped": 1, } - table = block.get("table") pending_fields = _get_pending_fields(block) - if not pending_fields: block["desc_done"] = True return { @@ -962,9 +884,7 @@ def _process_table_for_missing_descriptions( "consecutive_errors": 0, "skipped": 0, } - block["desc_done"] = False - try: result = _process_pending_field_batches( client=client, @@ -980,7 +900,6 @@ def _process_table_for_missing_descriptions( "consecutive_errors": 1, "skipped": 0, } - if result["updated_in_table"]: _mark_table_desc_done(block) @@ -1037,7 +956,6 @@ def _build_desc_prompt(table_name: str, field_names: List[str]) -> str: {json.dumps(field_names, ensure_ascii=False)} """.strip() - def _call_openai_desc_map_once(client, prompt: str): return client.chat.completions.create( model="gpt-4o-mini", @@ -1050,33 +968,25 @@ def _call_openai_desc_map_once(client, prompt: str): timeout=180, ) - def _smart_desc_map_openai(client, table_name: str, fields: List[Dict[str, Any]]) -> Dict[str, str]: if not client: return {} - field_names = _get_field_names(fields) if not field_names: return {} - prompt = _build_desc_prompt(table_name, field_names) - for attempt in range(3): try: response = _call_openai_desc_map_once(client, prompt) text = (response.choices[0].message.content or "").strip() - parsed = _extract_json_object(text) normalized = _normalize_desc_map(parsed) if normalized: return normalized - frappe.logger().warning( f"OpenAI returned non-JSON table={table_name} attempt={attempt+1} preview={text[:200]!r}" ) except Exception as e: frappe.logger().error(f"OpenAI error table={table_name} attempt={attempt+1}: {e}") - time.sleep(2 * (attempt + 1)) - return {} \ No newline at end of file diff --git a/changai/changai/api/v2/build_cards_faiss_index_v2.py b/changai/changai/api/v2/build_cards_faiss_index_v2.py index 881e032..05849ac 100644 --- a/changai/changai/api/v2/build_cards_faiss_index_v2.py +++ b/changai/changai/api/v2/build_cards_faiss_index_v2.py @@ -8,7 +8,7 @@ from langchain_core.documents import Document from langchain_community.docstore.in_memory import InMemoryDocstore from langchain_community.vectorstores import FAISS -from changai.changai.api.v2.text2sql_pipeline_v2 import get_embedding_engine +from changai.changai.api.v2.retrieve import get_embedding_engine import os import pickle diff --git a/changai/changai/api/v2/clients.py b/changai/changai/api/v2/clients.py new file mode 100644 index 0000000..57191a4 --- /dev/null +++ b/changai/changai/api/v2/clients.py @@ -0,0 +1,314 @@ +import frappe +import requests +import json +import time +from frappe import _ +from typing import Any, Dict, List, Optional, Union +from google import genai +from google.genai import types +from google.oauth2 import service_account +from google.api_core import exceptions as google_exceptions +from changai.changai.api.v2.schema_utils import (ChangAIConfig, CHANGAI_SETTINGS, CHANGAI_GUIDE_LINK, ERPGULF_LINK, settingsUrl) +_GEMINI_CLIENT = None +_GEMINI_CONFIG = None +APPLICATION_JSON = "application/json" +MODEL_ID = "gemini-2.5-flash-lite" +STATUS_200 = 200 + + +def call_model(prompt: str, task: str = "llm",sys_prompt: str = "") -> Any: + config = ChangAIConfig.get() + if config["REMOTE"] and config["llm"] == "QWEN3": + return remote_llm_request_deploy_test(prompt=prompt, task=task) + else: + if config["llm"] == "Gemini": + return call_gemini(prompt,sys_prompt) + + +def _post_json(url: str, headers: Dict[str, str], payload: Dict[str, Any], timeout: int = 120): + try: + res = requests.post(url, headers=headers, json=payload, timeout=timeout) + ct = (res.headers.get("Content-Type") or "").lower() + try: + body = res.json() if APPLICATION_JSON in ct else {"raw_text": res.text} + except Exception: + body = {"raw_text": res.text} + if res.status_code not in (STATUS_200, 201, 202): + return {"ok": False, "status_code": res.status_code, "body": body} + return {"ok": True, "status_code": res.status_code, "body": body} + except requests.exceptions.Timeout: + return {"ok": False, "status_code": None, "body": {"error": "timeout"}} + except Exception as e: + return {"ok": False, "status_code": None, "body": {"error": str(e)}} + + + + +def local_llm_request(prompt: str) -> str: + config = ChangAIConfig.get() + url = f"{config['URL'].rstrip('/')}/api/generate" + payload = {"model": config["LOCAL_LLM"], "prompt": prompt, "stream": False} + resp = _post_json(url, headers={}, payload=payload, timeout=120) + if not resp.get("ok"): + return f"Error: local LLM call failed ({resp.get('status_code')}): {resp.get('body')}" + text = (resp.get("body") or {}).get("response") + return (text or "").strip() or "Error: Empty response from local LLM." + + +def _get_gemini_vertex_config(config): + project_id = (config.get("gemini_project_id") or "").strip() + credentials_json = (config.get("gemini_json_content") or "").strip() + location = (config.get("gemini_location") or "").strip() + return project_id, credentials_json, location + + +def _throw_missing_vertex_field(project_id: str, location: str, credentials_json: str) -> None: + if not project_id: + frappe.throw( + _("Gemini Project ID is missing.

Please Go to Settings Page and enter your Gemini Project ID.
" + "Check Quick Start Guide 👇:
Click here
" + "ERPGulf.com.").format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), + title=_("Missing Gemini Project ID"), + ) + if not location: + frappe.throw( + _("Gemini Location is missing.

Please Go to Settings Page and enter your Gemini Location.
" + "Check Quick Start Guide 👇:
Click here
" + "ERPGulf.com.").format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), + title=_("Missing Gemini Location"), + ) + if not credentials_json: + frappe.throw( + _("Service Account Credentials are missing.

Please Go to Settings Page and enter your Service Account Credential.
" + "Check Quick Start Guide 👇:
Click here" + "ERPGulf.com." +).format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), + title=_("Missing Service Account Credentials"), + ) + + + +def _get_api_key_client(config): + try: + api_key = config.get("gemini_api_key") + except Exception: + api_key = None + + if not api_key: + frappe.throw( + _( + "Gemini API key is not configured.

" + "You have two options to authenticate with Gemini:

" + "Option 1 (Free / API Key):
" + "Go to ChangAI Settings and enter your Gemini API Key.
" + "Get your free API key from " + "Google AI Studio.

" + "Option 2 (Vertex AI / Service Account):
" + "Fill in Gemini Project ID, Gemini Location, " + "and Service Account Credentials in Got to Settings Page.
" + "ChangAI Quick Start Guide 👇:
" + "Click here
" + "ERPGulf.com." + + ).format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), + title=_("Gemini Authentication Not Configured"), + ) + + return genai.Client(api_key=api_key) + + +def _build_gemini_client(config): + project_id, credentials_json, location = _get_gemini_vertex_config(config) + + if project_id or credentials_json or location: + return _build_vertex_gemini_client(project_id, location, credentials_json) + + return _get_api_key_client(config) + + +def _build_gemini_contents(prompt: str): + return [ + { + "role": "user", + "parts": [{"text": str(prompt)}], + } + ] + + +def _clean_gemini_response_text(text: str) -> str: + text = (text or "").strip() + if text.startswith("```"): + text = text.replace("```json", "").replace("```", "").strip() + return text + + +def _handle_gemini_api_exception(e: Exception) -> None: + if isinstance(e, google_exceptions.ResourceExhausted): + frappe.throw( + _("Gemini API quota exceeded.

Please wait and try again or upgrade your plan.
Check Quick Start Guide 👇:
" + "Click here
" + "ERPGulf.com." +).format(CHANGAI_GUIDE_LINK,ERPGULF_LINK), + + title=_("Gemini Quota Exceeded"), + ) + if isinstance(e, google_exceptions.Unauthenticated): + frappe.throw( + _("Gemini API key is invalid.

Please go to ChangAI Settings and enter a valid Gemini API Key.
" + "Check ChangAI Quick Start Guide 👇:
Click here
" + "ERPGulf.com." +).format(CHANGAI_GUIDE_LINK,ERPGULF_LINK), + title=_("Invalid Gemini API Key"), + ) + if isinstance(e, google_exceptions.PermissionDenied): + frappe.throw( + _("Gemini API permission denied.

Please check your API key permissions.
" + "Check ChangAI Quick Start Guide 👇:
Click here
" + "ERPGulf.com." +).format(CHANGAI_GUIDE_LINK,ERPGULF_LINK), + title=_("Gemini Permission Denied"), + ) + if isinstance(e, google_exceptions.InvalidArgument): + frappe.throw( + _("Invalid request to Gemini API: {0}
" + "Check ChangAI Quick Start Guide 👇:
" + "Click here
" + "ERPGulf.com.").format(str(e),CHANGAI_GUIDE_LINK,ERPGULF_LINK), + title=_("Gemini Invalid Request"), + ) + + frappe.log_error(frappe.get_traceback(), "Gemini API Unexpected Error") + frappe.throw( + _("Gemini API error: {0}
" + "Check ChangAI Quick Start Guide 👇:
" + "Click here
" + "ERPGulf.com.").format(str(e),CHANGAI_GUIDE_LINK,ERPGULF_LINK), + title=_("Gemini API Error"), + ) + + +def gemini_client(): + global _GEMINI_CLIENT,_GEMINI_CONFIG + if _GEMINI_CLIENT is None: + config = frappe.get_single(CHANGAI_SETTINGS) + _GEMINI_CONFIG = config + _GEMINI_CLIENT = _build_gemini_client(config) + return _GEMINI_CLIENT + + + +def _build_input_payload(task: str, prompt: str, question: Optional[str], + db_result_json: Optional[str], user_message: Optional[str]) -> Dict[str, Any]: + if task == "format_db": + return {"task": "format_db", "question": question or "", "db_result_json": db_result_json or "{}"} + if task == "helpdesk_task": + return {"task": "helpdesk_task", "user_message": user_message or prompt or ""} + return {"task": "llm", "user_input": prompt} + + +def _poll_until_done(get_url: str, headers: Dict) -> Any: + terminal = {"succeeded", "failed", "canceled"} + deadline = time.time() + 300 + last = None + while time.time() < deadline: + try: + poll = requests.get(get_url, headers=headers, timeout=120).json() + except Exception as e: + poll = {"raw_text": str(e)} + last = poll + status = poll.get("status") + if status in terminal: + if status == "succeeded": + return poll.get("output") + return {"Error": f"Model ended with status {status}", "details": poll} + time.sleep(2) + return {"Error": "Polling timed out", "details": last} + + +def _build_vertex_gemini_client(project_id: str, location: str, credentials_json: str): + _throw_missing_vertex_field(project_id, location, credentials_json) + + service_account_info = json.loads(credentials_json) + creds = service_account.Credentials.from_service_account_info( + service_account_info, + scopes=["https://www.googleapis.com/auth/cloud-platform"], + ) + return genai.Client( + vertexai=True, + project=project_id, + location=location, + credentials=creds, + ) + + + +def remote_llm_request_deploy_test( + prompt: str = "", + task: str = "llm", + question: Optional[str] = None, + db_result_json: Optional[str] = None, + user_message: Optional[str] = None, +) -> Any: + config = ChangAIConfig.get() + headers = { + "Content-Type": APPLICATION_JSON, + "Prefer": "wait", + "Authorization": f"Bearer {config['API_TOKEN']}", + } + input_payload = _build_input_payload(task, prompt, question, db_result_json, user_message) + create = _post_json(config["deploy_url"], headers=headers, payload={"input": input_payload}, timeout=120) + + if not create.get("ok"): + return {"Error": "Create prediction failed", "status_code": create.get("status_code"), "details": create.get("body")} + + get_url = ((create.get("body") or {}).get("urls") or {}).get("get") + if not get_url: + return {"Error": "Missing get URL from deploy response", "details": create.get("body")} + + return _poll_until_done(get_url, headers) + + +def remote_embedder_request(formatted_q: str) -> Union[List[Any], str]: + config = ChangAIConfig.get() + payload = {"version": config["EMBED_VERSION_ID"], "input": {"user_input": formatted_q}} + headers = { + "Content-Type": APPLICATION_JSON, + "Prefer": "wait", + "Authorization": f"Bearer {config['API_TOKEN']}", + } + response = _post_json(config["URL"], headers, payload) + try: + if response: + return response["body"]["output"] + except Exception as e: + return "Error: " + str(e) + + +def call_model(prompt: str, task: str = "llm",sys_prompt: str = "") -> Any: + config = ChangAIConfig.get() + if config["REMOTE"] and config["llm"] == "QWEN3": + return remote_llm_request_deploy_test(prompt=prompt, task=task) + else: + if config["llm"] == "Gemini": + return call_gemini(prompt,sys_prompt) + + +def call_gemini(prompt: str,sys_prompt: str) -> Union[str, Dict[str, Any]]: + try: + # frappe.clear_document_cache(CHANGAI_SETTINGS) + client = gemini_client() + + gemini_config = types.GenerateContentConfig( + system_instruction=sys_prompt, + ) + response = client.models.generate_content( + model=MODEL_ID, + config=gemini_config, + contents=_build_gemini_contents(prompt), + ) + return _clean_gemini_response_text(response.text) + + except frappe.exceptions.ValidationError: + raise + except Exception as e: + _handle_gemini_api_exception(e) diff --git a/changai/changai/api/v2/create_qr.py b/changai/changai/api/v2/create_qr.py index ba8bc66..5b08ddd 100644 --- a/changai/changai/api/v2/create_qr.py +++ b/changai/changai/api/v2/create_qr.py @@ -11,9 +11,8 @@ from base64 import b64encode import io import os - - -def create_qr_code(doc): +@frappe.whitelist() +def create_qr_code(doc,method): """Create QR Code after inserting Employee""" if not hasattr(doc, 'custom_qr_code'): return diff --git a/changai/changai/api/v2/format_output.py b/changai/changai/api/v2/format_output.py index b162c55..c0f2562 100644 --- a/changai/changai/api/v2/format_output.py +++ b/changai/changai/api/v2/format_output.py @@ -1,6 +1,8 @@ from decimal import Decimal from typing import Any, Dict, List, Optional, Set import frappe +import json +from changai.changai.api.v2.clients import call_model,gemini_client import sqlglot from sqlglot import exp @@ -589,4 +591,64 @@ def local_format(sql: str, sample_rows: List[Dict[str, Any]]): result = format_sql_response(sql, row_count, sample_rows) return result +def format_data_conversationally(user_data: Any) -> str: + # Safe: CONVERSATION_TEMPLATE is a hardcoded internal template string. + # User SQL result is passed only as data context, not as template source. + # nosemgrep: frappe-semgrep-rules.rules.security.frappe-ssti + return render_template( + CONVERSATION_TEMPLATE, # nosemgrep: frappe-semgrep-rules.rules.security.frappe-ssti + + {"data": user_data} + ) + +def format_data(qstn: str, sql_data: Any) -> Dict[str, str]: + if isinstance(sql_data, (dict, list)): + db_result_json = json.dumps(sql_data, ensure_ascii=False, default=str) + else: + db_result_json = str(sql_data) if sql_data is not None else "{}" + + sys_prompt = """ +You are ChangAI, a warm and intelligent business assistant. +Your job is to turn raw database results into clear, friendly, human-readable answers. +CONTENT RULES: +- Use BOTH the user question and the DB result JSON to form the answer. +- Use ONLY values present in the JSON. NEVER invent numbers or fields. +- If result is empty, respond warmly and suggest refining the search. +- Do NOT mention SQL, tables, fields, JSON, reasoning, or steps. + +TONE & STYLE: +- Warm, conversational, and helpful — like a knowledgeable friend, not a report. +- If the question is in Arabic, reply in natural Arabic — not translated English. +- Never respond with a cold, empty, or robotic answer. + +FORMATTING: +- Start with ONE relevant emoji matching the topic (📦💰🧾👥📊📅🔍💤📉) +- For 3+ items, use a bullet list: • Item — value +- If list exceeds shown items, state exactly how many remain. +- Keep answers brief (1–6 lines). Lead with the direct answer, then light context. + +CLOSING: +- End with ONE short, relevant follow-up question to keep the conversation going. +- Make it feel natural, not robotic. +Never list names or items in a comma-separated line. Ever. +OUTPUT: +- Markdown ALLOWED: **bold**, • bullets, emojis +- i dont want too much gap between the texts also gaps are not allowed between items listed. +- No JSON. No code blocks. No labels. No explanations. +- Output ONLY the final user-facing answer. Nothing else. +- if the user question is in english reply in english only very important. +if the user question is in arabic respond in arabic only. and if the question is in english respond answer also english +""" + user_prompt=f""" + QUESTION: + {qstn} + + DATABASE_RESULT_JSON: + {db_result_json} + """ + output = call_model(user_prompt,"llm",sys_prompt) + answer = str(output) + return {"answer": answer} + + diff --git a/changai/changai/api/v2/fvs_stores/erpnext/emb_dir/field_docs.pkl b/changai/changai/api/v2/fvs_stores/erpnext/emb_dir/field_docs.pkl index c180c65..210e108 100644 Binary files a/changai/changai/api/v2/fvs_stores/erpnext/emb_dir/field_docs.pkl and b/changai/changai/api/v2/fvs_stores/erpnext/emb_dir/field_docs.pkl differ diff --git a/changai/changai/api/v2/fvs_stores/erpnext/report_fvs/index.faiss b/changai/changai/api/v2/fvs_stores/erpnext/report_fvs/index.faiss index 9812c61..1135cf5 100644 Binary files a/changai/changai/api/v2/fvs_stores/erpnext/report_fvs/index.faiss and b/changai/changai/api/v2/fvs_stores/erpnext/report_fvs/index.faiss differ diff --git a/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.faiss b/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.faiss index 88b1900..6f75735 100644 Binary files a/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.faiss and b/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.faiss differ diff --git a/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.pkl b/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.pkl index 18d1a15..dc1cc02 100644 Binary files a/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.pkl and b/changai/changai/api/v2/fvs_stores/erpnext/schema_fvs/index.pkl differ diff --git a/changai/changai/api/v2/fvs_stores/erpnext/table_fvs/index.faiss b/changai/changai/api/v2/fvs_stores/erpnext/table_fvs/index.faiss index 3d1bb78..34b68ec 100644 Binary files a/changai/changai/api/v2/fvs_stores/erpnext/table_fvs/index.faiss and b/changai/changai/api/v2/fvs_stores/erpnext/table_fvs/index.faiss differ diff --git a/changai/changai/api/v2/gdoc_ai.py b/changai/changai/api/v2/gdoc_ai.py new file mode 100644 index 0000000..e69de29 diff --git a/changai/changai/api/v2/helpdesk_api.py b/changai/changai/api/v2/helpdesk_api.py index cfad0d9..7e59433 100644 --- a/changai/changai/api/v2/helpdesk_api.py +++ b/changai/changai/api/v2/helpdesk_api.py @@ -1,7 +1,14 @@ import frappe from frappe import _ from werkzeug.wrappers import Response +from typing import Any, Optional, Dict import json +from changai.changai.api.v2.schema_utils import read_asset +from changai.changai.api.v2.clients import call_gemini + +SUPPORT_PROMPT = read_asset("support.txt", base="prompts") +SUPPORT_USER_PROMPT = read_asset("support_user_prompt.txt", base="prompts") +SUPPORT_SYS_PROMPT = read_asset("support_sys_prompt.txt", base="prompts") @frappe.whitelist() def create_helpdesk_ticket(subject:str,user:str,email:str,priority:str ="Low", ticket_type: str ="Bug"): try: @@ -130,3 +137,41 @@ def get_user_tickets(ticket_id: int =None): status=500, mimetype="application/json") + +@frappe.whitelist(allow_guest=False) +def support_bot(message: str) -> Dict[str, Any]: + user_email = frappe.session.user + full_name = frappe.get_value("User", frappe.session.user, "full_name") + prompt = SUPPORT_USER_PROMPT.format(user_message=message) + raw = call_gemini(prompt, SUPPORT_SYS_PROMPT) + output = json.loads(raw) + task_flag = (output.get("task_flag") or "UNKNOWN").strip() + ticket_id = output.get("ticket_id") + + if isinstance(ticket_id, str) and ticket_id.isdigit(): + ticket_id = int(ticket_id) + if not isinstance(ticket_id, int): + ticket_id = None + + if task_flag == "CREATE_TICKET": + try: + response = create_helpdesk_ticket(message, full_name, user_email) + return json.loads(response.get_data(as_text=True)) # ✅ unwrap Response → dict + except Exception as e: + return {"error": str(e)} + + if task_flag == "TICKET_DETAILS": + if not ticket_id: + return {"kind": "TICKET_DETAILS", "error": "Ticket id missing. Please say like: ticket 29"} + try: + response = get_user_tickets(ticket_id) + return json.loads(response.get_data(as_text=True)) # ✅ unwrap Response → dict + except Exception as e: + return {"error": str(e)} + + if task_flag == "GET_USER_TICKETS": + response = get_user_tickets() + return json.loads(response.get_data(as_text=True)) # ✅ unwrap Response → dict + + return {"kind": "UNKNOWN", "message": "Please describe the issue or provide a ticket number."} + diff --git a/changai/changai/api/v2/non_erp_handler.py b/changai/changai/api/v2/non_erp_handler.py index a2e6b54..161fbde 100644 --- a/changai/changai/api/v2/non_erp_handler.py +++ b/changai/changai/api/v2/non_erp_handler.py @@ -7,10 +7,13 @@ import pickle from dataclasses import dataclass from typing import Dict, List, Optional, Set, Tuple, Any +from changai.changai.api.v2.schema_utils import read_asset import frappe from rapidfuzz import process, fuzz - +_NON_ERP_DATA = None +_NON_ERP_QUESTIONS = None +_NON_ERP_RESPONSE_MAP = None @dataclass class ResponseEntry: @@ -597,4 +600,42 @@ def handle_non_erp_query(user_input: str) -> dict: "matcher_seconds": round(matcher_seconds, 6), "total_seconds": round(total_seconds, 6), } - } +} + +def non_erp_response(non_erp_q: str) -> Optional[str]: + questions, response_map = load_non_erp_data() + result = process.extractOne( + non_erp_q, + questions, + scorer=fuzz.WRatio, + score_cutoff=65 + ) + if not result: + return {"data":"Hey Iam ChangAI from ERPGulf,iam here to help you with your queries..."} + matched_q = result[0] + return {"data": response_map.get(matched_q, "Hey Iam ChangAI from ERPGulf,iam here to help you with your queries...")} + + +def load_non_erp_data(): + global _NON_ERP_DATA, _NON_ERP_QUESTIONS, _NON_ERP_RESPONSE_MAP + + if _NON_ERP_DATA is not None: + return _NON_ERP_QUESTIONS, _NON_ERP_RESPONSE_MAP + try: + _NON_ERP_DATA = read_asset("non_erp_combined.processed.json") + except Exception as e: + frappe.log_error(f"Failed to load NON-ERP data: {e}", "ChangAI NON-ERP Data Load Error") + _NON_ERP_DATA = [] + + _NON_ERP_QUESTIONS = [] + _NON_ERP_RESPONSE_MAP = {} + + for item in _NON_ERP_DATA: + q = item.get("user_input") + if not q: + continue + + _NON_ERP_QUESTIONS.append(q) + _NON_ERP_RESPONSE_MAP[q] = item.get("response") + + return _NON_ERP_QUESTIONS, _NON_ERP_RESPONSE_MAP diff --git a/changai/changai/api/v2/retrieve.py b/changai/changai/api/v2/retrieve.py new file mode 100644 index 0000000..f8a8e3b --- /dev/null +++ b/changai/changai/api/v2/retrieve.py @@ -0,0 +1,696 @@ +import os +import re +import json +import pickle +import shutil +from pathlib import Path +from functools import lru_cache +from typing import Any, Dict, List, Optional, Union +from rapidfuzz import fuzz, process +from changai.changai.api.v2.non_erp_handler import load_non_erp_data +from changai.changai.api.v2.clients import gemini_client +from changai.changai.api.v2.tts import get_polly_client +from changai.changai.api.v2.schema_utils import ( + ChangAIConfig, + CHANGAI_GUIDE_LINK, + ERPGULF_LINK, + settingsUrl, + format_schema_context, + publish_pipeline_update, + _safe_join, +) + +from changai.changai.api.v2.clients import ( + _post_json, + _GEMINI_CLIENT, + APPLICATION_JSON, +) +import numpy as np +import frappe +from frappe import _ +from huggingface_hub import snapshot_download +from langchain_community.vectorstores import FAISS +from langchain_huggingface import HuggingFaceEmbeddings +_KEYWORDS_LIST = None +_KEYWORDS_SET = None +_FIELD_DOCS_CACHE = None +_FIELD_EMBS_CACHE = None +_TABLE_TO_IDX_CACHE = None +_VS_REPORT=None +_VS_TABLE = None +_KEYWORDS_SET=None +_VS_MASTER = None +_EMBEDDER_INSTANCE = None +_FULL_FIELDS_VS = None +_SUB_VS_CACHE = {} +EMBEDDING_ENGINE_NONE_MESSG = f""" +Embedding engine is None. Model not loaded. +Check Quick Start Guide Here 👇: +{CHANGAI_GUIDE_LINK}""" +from changai.changai.api.v2.schema_utils import read_asset +bk = read_asset("business_keywords_v1.json", base="assets") +BUSINESS_KEYWORDS = bk.get("business_keywords", bk) + +@frappe.whitelist(allow_guest=False) +def download_model(): + frappe.enqueue( + "changai.changai.api.v2.retrieve.download_model_from_ui", # dot-path to the function + queue="long", # use "long" queue for heavy tasks + timeout=3600, # 1 hour timeout (in seconds) + is_async=True, # run in background (default True) + job_name="download_model", # optional: helps track/deduplicate jobs + ) + return { + "ok":True,"message":"Model Downloading.." + } + + +def _get_model_path(): + site_path = frappe.get_site_path("private", "files", "changai_model") + return site_path + + +@frappe.whitelist(allow_guest=False) +def download_model_from_ui(): + global _EMBEDDER_INSTANCE + + model_path = _get_model_path() + + try: + if os.path.exists(model_path): + shutil.rmtree(model_path) + + os.makedirs(model_path, exist_ok=True) + + snapshot_download( + repo_id="hyrinmansoor/changAI-nomic-embed-text-v1.5-finetuned", + local_dir=model_path, + ignore_patterns=[ + "*.pt", + "*.pth", + "*.bin", + "trainer_*", + "optimizer*" + ] + ) + _EMBEDDER_INSTANCE = None + return {"status": "success", "message": "Embedding model downloaded successfully."} + + except Exception as e: + frappe.log_error(frappe.get_traceback(), "Embedding Model Download Failed") + frappe.throw(_("Model download failed: {0}\n Check Quick Start Guide Here 👇:\n{1}
" + "Download Embedding Model.
" + "ERPGulf.com." +).format(str(e),CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK)) + + + +def load_field_matrix(): + global _FIELD_DOCS_CACHE, _FIELD_EMBS_CACHE, _TABLE_TO_IDX_CACHE + + if _FIELD_DOCS_CACHE is not None: + return _FIELD_DOCS_CACHE, _FIELD_EMBS_CACHE, _TABLE_TO_IDX_CACHE + + app_root = Path(frappe.get_app_path("changai")).resolve() + schema_rel = "changai/api/v2/fvs_stores/erpnext/emb_dir" + # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal + schema_path = _safe_join(app_root, schema_rel) + + embs_path = schema_path / "field_embs.npy" + docs_path = schema_path / "field_docs.pkl" + table_idx_path = schema_path / "table_to_idx.pkl" + + if not embs_path.exists(): + frappe.throw(f"Missing field_embs.npy. Rebuild schema FVS first: {embs_path}") + + # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal + with open(docs_path, "rb") as f: + docs = pickle.load(f) + + # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal + with open(table_idx_path, "rb") as f: + table_to_idx = pickle.load(f) + + embs = np.load(embs_path, mmap_mode="r") + + _FIELD_DOCS_CACHE = docs + _FIELD_EMBS_CACHE = embs + _TABLE_TO_IDX_CACHE = table_to_idx + + return docs, embs, table_to_idx + + +def get_embedding_engine(): + global _EMBEDDER_INSTANCE + if _EMBEDDER_INSTANCE is not None: + return _EMBEDDER_INSTANCE + + model_path = _get_model_path() # check path first, always + + if not os.path.exists(model_path): + _EMBEDDER_INSTANCE = None # reset if model missing + frappe.throw( + _( + "Go to ChangAI Settings and click" + "Download Embedding Model.

" + "Check this Quick Start Guide for more detail: " + "Click here" + "ERPGulf.com." + + ).format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), + title=_("Embedding Model Required") + ) + + if _EMBEDDER_INSTANCE is None: + _EMBEDDER_INSTANCE = HuggingFaceEmbeddings( + model_name=model_path, + model_kwargs={"device": "cpu","trust_remote_code": True,}, + encode_kwargs={ + "normalize_embeddings": True, + }, + ) + + return _EMBEDDER_INSTANCE + + + + +def get_vs(istable: bool): + global _VS_TABLE, _VS_REPORT + + emb = get_embedding_engine() + if emb is None: + frappe.throw(_(EMBEDDING_ENGINE_NONE_MESSG)) + + app_path = frappe.get_app_path("changai") + + if istable: + if _VS_TABLE is None: + table_vs_path = os.path.join( + app_path, "changai", "api", "v2", + "fvs_stores", "erpnext", "table_fvs" + ) + + if not os.path.exists(table_vs_path): + frappe.throw(_("FAISS table store not found at {0}").format(table_vs_path)) + + _VS_TABLE = FAISS.load_local( + table_vs_path, + emb, + allow_dangerous_deserialization=True + ) + + return _VS_TABLE + + else: + if _VS_REPORT is None: + report_vs_path = os.path.join( + app_path, "changai", "api", "v2", + "fvs_stores", "erpnext", "report_fvs" + ) + + if not os.path.exists(report_vs_path): + frappe.throw(_("FAISS report store not found at {0}").format(report_vs_path)) + + _VS_REPORT = FAISS.load_local( + report_vs_path, + emb, + allow_dangerous_deserialization=True + ) + + return _VS_REPORT + + +def get_master_vs(): + global _VS_MASTER + try: + if _VS_MASTER is None: + emb = get_embedding_engine() + if emb is None: + frappe.throw(_(EMBEDDING_ENGINE_NONE_MESSG)) + + master_vs_path = frappe.get_site_path( + "private", "changai", "fvs_stores", "erpnext", "masterdata_fvs" + ) + if not os.path.exists(master_vs_path): + frappe.throw(_( + "FAISS MASTER store not found at {0}.

" + "Please open " + "Go to Settings Page" + "and click on the Update Master Data button in the Training tab.

" + "Check Quick Start Guide Here 👇
" + "Click here


" + "ERPGulf.com" + + ).format( + master_vs_path, + settingsUrl, + CHANGAI_GUIDE_LINK, + ERPGULF_LINK + )) + + _VS_MASTER = FAISS.load_local( + master_vs_path, + emb, + allow_dangerous_deserialization=True + ) + except Exception as e: + frappe.log_error(f"Error loading master vector store: {e}", "ChangAI Master VS Load Error") + + return _VS_MASTER +_WARMUP_COUNT=0 +def load_on_startup(): + global _WARMUP_COUNT,_EMBEDDER_INSTANCE, _VS_TABLE, _FULL_FIELDS_VS, _VS_MASTER, _FIELD_DOCS_CACHE, _GEMINI_CLIENT + _WARMUP_COUNT+=1 + frappe.log_error( + title=f"ChangAI Warmup called | PID {os.getpid()} | Count {_WARMUP_COUNT}", + message="load_on_startup triggered" + ) + # If all are already loaded, skip + if all([ + _EMBEDDER_INSTANCE is not None, + _VS_TABLE is not None, + _FULL_FIELDS_VS is not None, + _VS_MASTER is not None, + _FIELD_DOCS_CACHE is not None ]): + frappe.log_error( + title=f"ChangAI Warmup skipped | PID {os.getpid()}", + message="Already loaded in this worker" + ) + return + message=f"PID={os.getpid()} | module={__name__} | file={__file__} | loaded={_EMBEDDER_INSTANCE is not None} | id={id(_EMBEDDER_INSTANCE)}" + try: + load_non_erp_data() + get_embedding_engine() + get_vs(True) + load_field_matrix() + gemini_client() + get_master_vs() + _init_keywords() + config = ChangAIConfig.get() + get_polly_client(config) + frappe.log_error( + title="ChangAI Warmup Completed", + message=frappe.get_traceback() # full stack trace + ) + except Exception as e: + frappe.log_error( + title="ChangAI Warmup Failed", + message=frappe.get_traceback() # full stack trace + ) + return message +@lru_cache(maxsize=None) +def _word_is_erp(word: str) -> bool: + if len(word) <= 3: + return False + if word in _KEYWORDS_SET: + return True + for kw in _KEYWORDS_SET: + if word in kw or kw in word: + return True + if len(word) >= 4: + match = process.extractOne( + word, _KEYWORDS_LIST, scorer=fuzz.ratio, score_cutoff=70 + ) + if match: + return True + return False +def _init_keywords(): + global _KEYWORDS_SET, _KEYWORDS_LIST + if not _KEYWORDS_SET: + _KEYWORDS_SET = set(kw.lower() for kw in BUSINESS_KEYWORDS) + _KEYWORDS_LIST = list(_KEYWORDS_SET) + # ✅ pre-warm cache — run every keyword through _word_is_erp at startup + for kw in _KEYWORDS_LIST: + _word_is_erp(kw) # result gets cached — first real request is instant + +def check_memory_status() -> dict: + return { + "pid": os.getpid(), + "module": __name__, + "file": __file__, + "globals": { + "embedding_model": { + "loaded": _EMBEDDER_INSTANCE is not None, + "id": id(_EMBEDDER_INSTANCE), + }, + "table_vs": { + "loaded": _VS_TABLE is not None, + "id": id(_VS_TABLE), + }, + "full_fields_vs": { + "loaded": _FULL_FIELDS_VS is not None, + "id": id(_FULL_FIELDS_VS), + }, + "field_docs": { + "loaded": _FIELD_DOCS_CACHE is not None, + "id": id(_FIELD_DOCS_CACHE), + }, + "field_embs": { + "loaded": _FIELD_EMBS_CACHE is not None, + "id": id(_FIELD_EMBS_CACHE), + }, + "table_to_idx": { + "loaded": _TABLE_TO_IDX_CACHE is not None, + "id": id(_TABLE_TO_IDX_CACHE), + }, + "master_vs": { + "loaded": _VS_MASTER is not None, + "id": id(_VS_MASTER), + }, + "gemini_client": { + "loaded": _GEMINI_CLIENT is not None, + "id": id(_GEMINI_CLIENT), + }, + # "symspell": { + # "loaded": sym_spell is not None, + # "id": id(sym_spell), + # }, + # "keywords": { + # "loaded": _KEYWORDS_SET is not None, + # "id": id(_KEYWORDS_SET), + # }, + } + } + + +@lru_cache(maxsize=512) +def _get_cached_embedding(q: str, request_id: str) -> tuple: + # vec = get_local_embedding(q) + emb = get_embedding_engine() + + publish_pipeline_update( + request_id, + "embedding_end", + "get_embedding_engine ended" + ) + vec = emb.embed_query(q) + publish_pipeline_update( + request_id, + "embedding_query_done", + "embedding query done" + ) + return tuple(vec) # tuple for hashability + + + +def call_fvs_table_search(get_table: bool, q: str, request_id: str) -> List[str]: + # get cached embedding + publish_pipeline_update( + request_id, + "Inside the Table Search Function", + _("Inside the Table Search Function") + ) + q_vec = np.array(_get_cached_embedding(q,request_id), dtype="float32") + publish_pipeline_update( + request_id, + "Completed Embed for Table Search Function", + _("Completed Embed for Table Search Function") + ) + + # use FAISS index directly instead of similarity_search + publish_pipeline_update( + request_id, + "q_vec_ready", + _("q_vec_ready") + ) + vs = get_vs(get_table) + publish_pipeline_update( + request_id, + "vs_ready", + _("vs_ready") + ) + scores, indices = vs.index.search(q_vec.reshape(1, -1), k=20) + publish_pipeline_update( + request_id, + "index_search_done", + _("index_search_done") + ) + + out, seen = [], set() + for idx in indices[0]: + if idx == -1: + continue + doc_id = vs.index_to_docstore_id[idx] + doc = vs.docstore.search(doc_id) + t = doc.metadata.get("table") if get_table else doc.metadata.get("report_name") + if t and t not in seen: + seen.add(t) + out.append(t) + return out + + + +def call_fvs_field_search_global_k( + user_question: str, + selected_tables: List[str], + k_total: int = 40, + request_id: Optional[str] = None +) -> str: + if isinstance(selected_tables, str): + try: + selected_tables = json.loads(selected_tables) + except Exception: + selected_tables = [selected_tables] + if not user_question or not selected_tables: + return "" + + docs, embs, table_to_idx = load_field_matrix() + + q_vec = np.array( + _get_cached_embedding(user_question, request_id), + dtype="float32" + ) + + q_vec = q_vec / max(np.linalg.norm(q_vec), 1e-12) + + all_idxs = [] + + for t in selected_tables: + t = str(t).strip() + if not t: + continue + + candidates = [ + t, + f"tab{t}" if not t.startswith("tab") else t, + t.replace("tab", "", 1) if t.startswith("tab") else t, + ] + + for key in candidates: + if key in table_to_idx: + all_idxs.extend(table_to_idx[key]) + break + + if not all_idxs: + frappe.log_error( + title="ChangAI Field Search: No Indexes Found", + message=json.dumps({ + "user_question": user_question, + "selected_tables": selected_tables, + "sample_table_to_idx_keys": list(table_to_idx.keys())[:50], + }, indent=2, default=str) + ) + return "" + + sub_embs = embs[all_idxs] + scores = sub_embs @ q_vec + + top_global = np.argsort(-scores)[:k_total] + + grouped = {} + seen = set() + + for i in top_global: + doc_i = all_idxs[int(i)] + d = docs[doc_i] + + meta = getattr(d, "metadata", {}) or {} + + is_table = meta.get("is_table") + table = meta.get("table") + field = meta.get("field") or meta.get("name") + + if not table or not field: + continue + + key = (table, field) + if key in seen: + continue + + seen.add(key) + + name = field + + join_hint = meta.get("join_hint") + if isinstance(join_hint, dict): + linked_table = join_hint.get("table") + if linked_table: + name += f" -> {linked_table}" + elif isinstance(join_hint, str) and join_hint.strip(): + name += f" -> {join_hint.strip()}" + + opts = meta.get("options") + if opts: + if isinstance(opts, list): + name += " {" + ", ".join(str(o) for o in opts[:5]) + "}" + else: + name += " {" + str(opts) + "}" + + grouped.setdefault(table, { + "is_table": is_table, + "fields": [] + }) + + grouped[table]["fields"].append(name) + + if not grouped: + frappe.log_error( + title="ChangAI Field Search: Empty Grouped Result", + message=json.dumps({ + "user_question": user_question, + "selected_tables": selected_tables, + "all_idxs_count": len(all_idxs), + "top_global_count": len(top_global), + }, indent=2, default=str) + ) + return "" + return format_schema_context(grouped) + +def call_retrieve_multi_line(user_question: str, request_id: str) -> Dict[str, Any]: + try: + top_tables = call_fvs_table_search(True, user_question, request_id) + publish_pipeline_update( + request_id, + "table_retrieval_done", + _("Tables retrieved") + ) + fields_candidates= call_fvs_field_search_global_k( + user_question, + selected_tables=top_tables, + k_total=40, + request_id=request_id + ) + publish_pipeline_update( + request_id, + "field_retrieval_done", + "Fields selected" + ) + return { + "selected_fields": fields_candidates, + "selected_tables": top_tables, + "top_tables": top_tables, + "top_fields": fields_candidates, + } + except frappe.exceptions.ValidationError: + raise + except Exception as e: + return {"selected_fields": {}, "selected_tables": [], "top_tables": [], "error": str(e)} + +def debug_entity_retriever(q: str,state:Dict): + resp = remote_entity_embedder(q) # this returns {"ok":..., "body":...} + return { + "query": q, + "raw_response": resp, + "parsed_entity_cards": call_entity_retriever(False, q,state) + } + +def remote_entity_embedder(q: str) -> Union[list, str]: + config = ChangAIConfig.get() + payload = {"version": config["entity_retriever"], "input": {"query": q}} + headers = { + "Content-Type": APPLICATION_JSON, + "Prefer": "wait", + "Authorization": f"Bearer {config['API_TOKEN']}", + } + response = _post_json(config["URL"], headers, payload) + return response + + + +def append_entity_field_to_schema(top_fields: str, table_name: str, field_name: str) -> str: + """ + Append field_name to the FIELDS section of table_name if missing. + Example: append customer_name into TABLE: tabCustomer block. + """ + + pattern = rf"(TABLE:\s*{re.escape(table_name)}\n.*?FIELDS:\n)(.*?)(?=\n\nTABLE:|\Z)" + + def replace_block(match): + header = match.group(1) + fields_block = match.group(2) + + # already exists + if re.search(rf"^- {re.escape(field_name)}(\s|$)", fields_block, re.MULTILINE): + return match.group(0) + + return header + fields_block.rstrip() + f"\n- {field_name}\n" + + return re.sub(pattern, replace_block, top_fields, count=1, flags=re.DOTALL) + + +def local_entity_embedder(q: str) -> List[Dict[str, Any]]: + hits = get_master_vs().similarity_search(q, k=20) + out, seen = [], set() + for h in hits: + entity_type = h.metadata.get("entity_type") # example: tabCustomer + entity_id = h.metadata.get("entity_id") # example: customer_name + entity_label = h.metadata.get("entity_label") + # if entity_type in state["selected_tables"]: + # state["selected_fields"] = append_entity_field_to_schema( + # top_fields=state["selected_fields"], + # table_name=entity_type, + # field_name=entity_id + # ) + + key = (entity_type, entity_label) + if key not in seen: + seen.add(key) + out.append({"entity_type": entity_type, "entity_id": entity_id, "entity_label": entity_label}) + return out + + +def call_entity_retriever(isreport: bool, qstn: str, state: Dict) -> Dict[str, Any]: + config = ChangAIConfig.get() + if config["REMOTE"] and config["llm"] == "QWEN3": + response = remote_entity_embedder(qstn) + + if not response.get("ok"): + frappe.log_error(f"Entity retriever failed: {response.get('body')}", "ChangAI Entity Retriever") + return {"raw": response, "cards": []} + + body = response.get("body") or {} + output = body.get("output") or {} + results = output.get("results") or [] + + cards = [r.get("entity_label") for r in results if r.get("entity_label")] + + return {"raw": body, "cards": cards} + else: + from changai.changai.api.v2.schema_utils import phonetic_match + entity_words = state.get("entity_words") + cards = [] + debug=[] + if entity_words is None: + return {"cards":[]} + for word in entity_words: + result = phonetic_match(isreport, word) + labels = result.get("entity_labels") or [] + debug.append({ + "word": word, + "result": result, + "labels": labels + }) + for label in labels: + if isreport: + try: + table_field, _ = label.split(":", 1) + table, field = table_field.split(".", 1) + doctype = table.removeprefix("tab") + state["doc"] = doctype + except Exception: + pass + if label and label not in cards: + cards.append(label) + return {"cards": cards} + diff --git a/changai/changai/api/v2/schema_utils.py b/changai/changai/api/v2/schema_utils.py index ffa9856..3756a91 100644 --- a/changai/changai/api/v2/schema_utils.py +++ b/changai/changai/api/v2/schema_utils.py @@ -1,23 +1,58 @@ -import sqlglot -from sqlglot import exp -from sqlglot import optimizer -from sqlglot.schema import MappingSchema -import frappe -from sqlglot.errors import ParseError, OptimizeError -from sqlglot.optimizer.qualify import qualify import json -from typing import Any, Dict, List, Tuple, Union, Optional, Set +import re +from collections import OrderedDict, defaultdict +from functools import lru_cache +from pathlib import Path +from typing import Any, Dict, List, Optional, Set, Tuple, Union +import frappe +import sqlglot import yaml -from frappe.utils import getdate from frappe import _ -from pathlib import Path -from collections import OrderedDict, defaultdict - -_PHONETIC_BUCKETS = defaultdict(list) +from frappe.utils import getdate +from sqlglot import exp, optimizer +from sqlglot.errors import ParseError, OptimizeError +from sqlglot.optimizer.qualify import qualify +from sqlglot.schema import MappingSchema import jellyfish from rapidfuzz import fuzz, process -_VALUE_TO_FIELD = {} +_VALUE_TO_FIELD = {} +CHANGAI_GUIDE_LINK="https://app.erpgulf.com/en/articles/chang-ai-quick-start-guide" +ERPGULF_LINK = "https://app.erpgulf.com/en/products/chang-ai-an-ai-agent" +settingsUrl = frappe.utils.get_url( + "/app/changai-settings/ChangAI%20Settings" +) +CHANGAI_SETTINGS = "ChangAI Settings" +_ASSETS_DIR = Path(frappe.get_app_path("changai", "changai", "api", "v2", "assets")).resolve() +_PROMPTS_DIR = Path(frappe.get_app_path("changai", "changai", "prompts")).resolve() +_PHONETIC_BUCKETS = defaultdict(list) +_ALLOWED_EXT = {".json", ".yaml",".j2", ".yml", ".txt", ".md"} +RAG_FOLDER = "Home/RAG Sources" +JSON_EXT = ".json" +YAML_EXT = ".yaml" + +def get_report_filter_fields(report_name: str): + try: + script = get_script(report_name).get("script") or "" + except Exception: + return [] + fieldnames = re.findall( + r'fieldname\s*:\s*["\']([^"\']+)["\']', + script + ) + return "|".join(dict.fromkeys(fieldnames)) +def match_report_intent(report_intent: str): + choices = list(REPORT_INTENT_MAP.keys()) + match = process.extractOne( + report_intent.lower(), + choices, + scorer=fuzz.WRatio, + score_cutoff=75 + ) + if not match: + return "" + matched_intent = match[0] + return REPORT_INTENT_MAP[matched_intent] def phonetic_bucket(): global _PHONETIC_BUCKETS, _VALUE_TO_FIELD @@ -37,32 +72,23 @@ def phonetic_bucket(): @frappe.whitelist(allow_guest=False) def phonetic_match(isreport: bool, word: str, threshold: int = 60): global _PHONETIC_BUCKETS, _VALUE_TO_FIELD - original_word = word - candidates = [] seen = set() - phonetic_bucket() - - # check EVERY word in the query for token in original_word.split(): if len(token) <= 2: continue - key = jellyfish.metaphone(token) - for value in _PHONETIC_BUCKETS.get(key, []): if value not in seen: seen.add(value) candidates.append(value) - if not candidates: return { "entity_labels": [], "reason": "no phonetic candidates found" } - result = process.extract( original_word, candidates, @@ -70,37 +96,17 @@ def phonetic_match(isreport: bool, word: str, threshold: int = 60): limit=5, score_cutoff=threshold ) - results = [] - for match, score, _ in result: label = _VALUE_TO_FIELD.get(match) - if label: results.append(label) - return { "entity_labels": results, "reason": "phonetic match found" } - -def _safe_join(base: Path, rel: str) -> Path: - """ - Prevent path traversal. Only allow reading inside base directory. - """ - p = (base / rel).resolve() - if base != p and base not in p.parents: - frappe.throw(_("Unsafe path: {0}").format(rel)) - return p - -_ALLOWED_EXT = {".json", ".yaml",".j2", ".yml", ".txt", ".md"} -_ASSETS_DIR = Path(frappe.get_app_path("changai", "changai", "api", "v2", "assets")).resolve() -_PROMPTS_DIR = Path(frappe.get_app_path("changai", "changai", "prompts")).resolve() -RAG_FOLDER = "Home/RAG Sources" -JSON_EXT = ".json" -YAML_EXT = ".yaml" def _get_file_doc_by_name(file_name: str, folder: str = RAG_FOLDER) -> Optional["frappe.model.document.Document"]: file_id = frappe.db.get_value("File", {"file_name": file_name, "folder": folder}, "name") if not file_id: @@ -127,48 +133,6 @@ def _read_filedoctype(file_name: str, folder: str = RAG_FOLDER): return raw -def read_asset(file_name: str, base: str = "assets") -> Any: - """ - base: - - "assets" -> changai/changai/api/v2/assets - - "prompts" -> changai/changai/prompts - """ - file_name = (file_name or "").strip() - if not file_name: - frappe.throw(_("file_name is required")) - - ext = Path(file_name).suffix.lower() - if ext not in _ALLOWED_EXT: - frappe.throw(_("Unsupported file type: {0}").format(ext)) - - if base == "assets": - root = _ASSETS_DIR - elif base == "prompts": - root = _PROMPTS_DIR - else: - root = None - if root is None: - frappe.throw(_("Invalid base: {0}").format(base)) - - path = _safe_join(root, file_name) - - if not path.is_file(): - frappe.throw(_("File not found: {0}").format(str(path))) - - content = path.read_text(encoding="utf-8", errors="replace") - - if ext == ".json": - try: - return json.loads(content) - except json.JSONDecodeError as e: - frappe.throw(_("Invalid JSON in {0}: {1}").format(str(path), str(e))) - if ext == ".yaml" or ext == ".yml": - try: - return yaml.safe_load(content) - except yaml.YAMLError as e: - frappe.throw(_("Invalid YAML in {0}: {1}").format(str(path), str(e))) - return content - def _load_mapping_data() -> dict: return read_asset("metaschema_clean_v2.json") @@ -384,3 +348,654 @@ def convert_yaml_schema_to_sqlglot_meta() -> dict: "ok": False, "message": str(e) } + + +@lru_cache(maxsize=512) +def is_child_table(table: str) -> bool: + doctype = table.replace("tab", "", 1) if table.startswith("tab") else table + + try: + meta = frappe.get_meta(doctype, cached=True) + return bool(getattr(meta, "istable", 0)) + except Exception: + return False + +CHILD_GENERIC_FIELDS = ["parent", "parenttype", "parentfield", "idx"] +MAIN_GENERIC_FIELDS = ["name", "docstatus"] +def enrich_fields_for_sql_context(table: str, fields: list[str]) -> list[str]: + out = list(fields) + + if is_child_table(table): + for f in reversed(CHILD_GENERIC_FIELDS): + if f not in out: + out.insert(0, f) + else: + for f in reversed(MAIN_GENERIC_FIELDS): + if f not in out: + out.insert(0, f) + + return out + + +def publish_pipeline_update(request_id, stage, message, data=None, done=False, error=False): + if not request_id: + return + payload = { + "request_id": request_id, + "stage": stage, + "message": message, + "data": data or {}, + "done": done, + "error": error, + "timestamp": frappe.utils.now_datetime().isoformat(), + } + frappe.publish_realtime( + event=f"debug_{request_id}", + message=payload, + user=frappe.session.user, + ) + + +def format_schema_context(grouped: dict) -> str: + parts = [] + + for table, table_data in grouped.items(): + if isinstance(table_data, dict): + raw_fields = table_data.get("fields", []) + is_table_value = table_data.get("is_table") + + if is_table_value is None: + child = is_child_table(table) + else: + child = bool(is_table_value) + else: + raw_fields = table_data + child = is_child_table(table) + + fields = enrich_fields_for_sql_context(table, raw_fields) + + parts.append(f"TABLE: {table}") + parts.append(f"TYPE: {'Child Table' if child else 'Main Table'}") + + if child: + parts.append("JOIN RULES:") + parts.append("- parent = parent document name") + parts.append("- parenttype = parent DocType") + parts.append("- parentfield = child table fieldname") + + parts.append("FIELDS:") + for field in fields: + parts.append(f"- {field}") + + parts.append("") + + return "\n".join(parts) + + +def _safe_join(base: Path, rel: str) -> Path: + """ + Prevent path traversal. Only allow reading inside base directory. + """ + p = (base / rel).resolve() + if base != p and base not in p.parents: + frappe.throw(_("Unsafe path: {0}\n" + "Check Quick Start Guide Here 👇:\n {1}").format(rel,CHANGAI_GUIDE_LINK)) + return p + + +def read_asset(file_name: str, base: str = "assets") -> Any: + """ + base: + - "assets" -> changai/changai/api/v2/assets + - "prompts" -> changai/changai/prompts + """ + file_name = (file_name or "").strip() + if not file_name: + frappe.throw(_("file_name is required\n" + "Check Quick Start Guide Here 👇:\n {0}").format(CHANGAI_GUIDE_LINK)) + ext = Path(file_name).suffix.lower() + if ext not in _ALLOWED_EXT: + frappe.throw(_("Unsupported file type: {0}\n" + "Check Quick Start Guide Here 👇:\n {1}").format(ext, CHANGAI_GUIDE_LINK)) + + if base == "assets": + root = _ASSETS_DIR + elif base == "prompts": + root = _PROMPTS_DIR + else: + root = None + if root is None: + frappe.throw(_("Invalid base: {0}\n" + "Check Quick Start Guide Here 👇:\n {1}").format(base, CHANGAI_GUIDE_LINK)) + # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal + path = _safe_join(root, file_name) + if not path.is_file(): + frappe.throw(_("File not found: {0}\n" + "Check Quick Start Guide Here 👇:\n {1}").format(str(path), CHANGAI_GUIDE_LINK)) + content = path.read_text(encoding="utf-8", errors="replace") + if ext == ".json": + try: + return json.loads(content) + except json.JSONDecodeError as e: + frappe.throw(_("Invalid JSON in {0}: {1}" + "Check Quick Start Guide Here 👇:\n {2}").format(str(path), str(e), CHANGAI_GUIDE_LINK)) + if ext == ".yaml" or ext == ".yml": + try: + return yaml.safe_load(content) + except yaml.YAMLError as e: + frappe.throw(_("Invalid YAML in {0}: {1}" + "Check Quick Start Guide Here 👇:\n {2}").format(str(path), str(e), CHANGAI_GUIDE_LINK)) + return content + +REPORT_INTENT_MAP = read_asset("report_intent_map.json",base="assets") + +class ChangAIConfig: + @classmethod + def get(cls): + if not hasattr(frappe.local, "_changai_config"): + frappe.clear_document_cache(CHANGAI_SETTINGS) + frappe.local._changai_config = get_settings() + return frappe.local._changai_config + + +def _build_frontend_settings_config() -> Dict[str, Any]: + settings = frappe.get_single(CHANGAI_SETTINGS) + aws_access_key_id = (getattr(settings, "aws_access_key_id", None) or "").strip() + aws_secret_access_key = (getattr(settings, "aws_secret_access_key", None) or "").strip() + aws_region = ( + getattr(settings, "aws_region", None) + or getattr(settings, "aws_default_region", None) + or "us-east-1" + ) + + return { + "RETAIN_MEM": settings.retain_memory, + "LLM_VERSION_ID": settings.llm_version_id, + "EMBED_VERSION_ID": settings.embedder_version_id, + "REMOTE": bool(settings.remote), + "deploy_url": settings.deploy_url, + "entity_retriever": settings.entity_retriever, + "support_api_url": settings.support_url, + "get_ticket_details_url": settings.get_ticket_details_url, + "llm": settings.llm, + "location": settings.gemini_location, + "retriever_structure": settings.retriever_structure, + "gemini_project_id": settings.gemini_project_id, + "gemini_json_content": settings.gemini_json_content, + "enable_voice_chat": bool(settings.enable_voice_chat), + "aws_region": aws_region, + "polly_voice_id": "Zayd", + "polly_enabled": bool(settings.enable_voice_chat and aws_access_key_id and aws_secret_access_key), + "enable_changai": bool(settings.enable_changai) + } + + +@frappe.whitelist(allow_guest=False) +def get_settings() -> Dict[str, Any]: + settings = frappe.get_single(CHANGAI_SETTINGS) + config = { + "RETAIN_MEM": settings.retain_memory, + "LLM_VERSION_ID": settings.llm_version_id, + "EMBED_VERSION_ID": settings.embedder_version_id, + "API_TOKEN": settings.api_token, + "REMOTE": bool(settings.remote), + "deploy_url": settings.deploy_url, + "entity_retriever": settings.entity_retriever, + "support_api_url": settings.support_url, + "get_ticket_details_url": settings.get_ticket_details_url, + "llm": settings.llm, + "location": settings.gemini_location, + "retriever_structure": settings.retriever_structure, + "gemini_project_id": settings.gemini_project_id, + "gemini_json_content": settings.gemini_json_content, + "aws_access_key_id": settings.aws_access_key_id, + "aws_secret_access_key": settings.aws_secret_access_key, + "enable_voice_chat": settings.enable_voice_chat, + } + return config + + +@frappe.whitelist(allow_guest=False) +def get_frontend_settings() -> Dict[str, Any]: + return _build_frontend_settings_config() + +def clean_sql(s: Any) -> str: + if isinstance(s, dict): + s = s.get("output") or s.get("sql") or s.get("text") or json.dumps(s, ensure_ascii=False, default=str) + elif isinstance(s, list): + s = "\n".join(str(x) for x in s) + else: + s = "" if s is None else str(s) + s = s.strip() + if s.startswith("```"): + first_newline = s.find("\n") + if first_newline != -1: + header = s[:first_newline].strip().lower() + if header in {"```", "```sql"}: + s = s[first_newline + 1 :].lstrip() + stripped = s.rstrip() + if stripped.endswith("```"): + stripped = stripped[:-3].rstrip() + s = stripped + if s[:3].lower() == "sql" and (len(s) == 3 or s[3].isspace()): + s = s[3:].lstrip() + return s.strip() + +def _parse_sql_ast(sql_text: str, dialect: str): + try: + return sqlglot.parse_one(sql_text, read=dialect), None + except Exception as e: + return None, str(e) + + +def _extract_tables(ast) -> Tuple[List[str], Dict[str, str]]: + base_tables = [] + alias_to_table = {} + for t in ast.find_all(exp.Table): + if not t.name: + continue + base_tables.append(t.name) + a = t.args.get("alias") + if a and a.name: + alias_to_table[a.name] = t.name + return list(dict.fromkeys(base_tables)), alias_to_table + + +def _extract_derived_aliases(ast) -> Set[str]: + derived = set() + for sq in ast.find_all(exp.Subquery): + a = sq.args.get("alias") + if a and a.name: + derived.add(a.name) + for cte in ast.find_all(exp.CTE): + a = cte.args.get("alias") + if a and a.name: + derived.add(a.name) + return derived + + +def _extract_select_aliases(ast) -> Set[str]: + aliases = set() + for sel in ast.find_all(exp.Select): + for proj in sel.expressions: + if isinstance(proj, exp.Alias) and proj.alias: + aliases.add(proj.alias) + return aliases + + +def _validate_qualified_col(col_name: str, qual: str, mapping: Dict, + alias_to_table: Dict, derived_aliases: Set) -> Optional[Tuple]: + if col_name == "*" or qual in derived_aliases: + return None + if qual in mapping: + if col_name not in mapping[qual]: + return (f"{qual}.{col_name}", qual) + return None + if qual in alias_to_table: + real = alias_to_table[qual] + if real in mapping and col_name not in mapping[real]: + return (f"{qual}.{col_name}", real) + return None + return (f"{qual}.{col_name}", None) + + +def _validate_unqualified_col(col_name: str, base_tables_set: Set, + mapping: Dict, select_aliases: Set, + unknown_cols: List, ambiguous: Set): + if col_name in select_aliases: + return + candidates = [t for t in base_tables_set if col_name in mapping.get(t, [])] + if len(candidates) == 0: + unknown_cols.append((col_name, None)) + elif len(candidates) > 1: + ambiguous.add(col_name) + + +def _validate_columns(ast, mapping: Dict, alias_to_table: Dict, + derived_aliases: Set, select_aliases: Set, + base_tables_set: Set) -> Tuple[List, Set]: + unknown_cols: List[Tuple[str, str]] = [] + ambiguous: Set[str] = set() + for col in ast.find_all(exp.Column): + if not col.name: + continue + if col.table: + result = _validate_qualified_col( + col.name, str(col.table), mapping, alias_to_table, derived_aliases + ) + if result: + unknown_cols.append(result) + else: + _validate_unqualified_col( + col.name, base_tables_set, mapping, select_aliases, unknown_cols, ambiguous + ) + return unknown_cols, ambiguous + + +def validate_sql_against_mapping( + sql_text: str, + mapping: Dict[str, List[str]], + dialect: str = "mysql" +) -> Dict[str, Any]: + result = { + "ok": True, + "unknown_tables": [], + "unknown_columns": [], + "ambiguous_columns": [], + "details": {"from_tables": [], "alias_to_table": {}, "derived_aliases": [], "select_aliases": []}, + } + ast, parse_error = _parse_sql_ast(sql_text, dialect) + if parse_error: + result["ok"] = False + result["details"]["parse_error"] = parse_error + return result + base_tables, alias_to_table = _extract_tables(ast) + derived_aliases = _extract_derived_aliases(ast) + select_aliases = _extract_select_aliases(ast) + result["details"]["from_tables"] = base_tables + result["details"]["alias_to_table"] = alias_to_table + result["details"]["derived_aliases"] = sorted(derived_aliases) + result["details"]["select_aliases"] = sorted(select_aliases) + unknown_tables = [t for t in base_tables if t not in mapping] + if unknown_tables: + result["ok"] = False + result["unknown_tables"] = unknown_tables + + unknown_cols, ambiguous = _validate_columns( + ast, mapping, alias_to_table, derived_aliases, select_aliases, set(base_tables) + ) + if unknown_cols or ambiguous: + result["ok"] = False + result["unknown_columns"] = unknown_cols + result["ambiguous_columns"] = sorted(ambiguous) + + return result +def _collect_docs(hits: Union[List[Any], Dict, str]) -> List[Tuple[str, Dict]]: + def _to_txt_md(doc: Any) -> Tuple[str, Dict]: + if isinstance(doc, dict): + return doc.get("text", "") or "", doc.get("metadata", {}) or {} + if isinstance(doc, str): + return doc, {} + return "", {} + if isinstance(hits, dict) and "message" in hits and isinstance(hits["message"], list): + hits = hits["message"] + if isinstance(hits, (dict, str)) or hasattr(hits, "page_content"): + return [_to_txt_md(hits)] + return [_to_txt_md(d) for d in (hits or [])] + + +def _parse_tag(txt: str, tag: str) -> str: + m = re.search(rf"\[{re.escape(tag)}\]\s*(.+?)(?:\s*\||\s*$)", txt or "") + return m.group(1).strip() if m else "" + +def _infer_type(txt: str) -> str: + if not (txt or "").startswith("["): + return "" + order = [ + ("TABLE", "table"), ("FIELD", "field"), ("JOIN", "join"), + ("METRIC", "metric"), ("ENUM", "enum"), ("PERIOD", "period"), + ("CURRENCY", "currency"), ("ENTITY", "entity") + ] + for tg, tp in order: + if txt.startswith(f"[{tg}]"): + return tp + return "" + +class _SchemaAccumulator: + def __init__(self): + self.tables: List[str] = [] + self.fields_by_table: Dict[str, List[str]] = OrderedDict() + self.joins: List[str] = [] + self.metrics: List[Tuple[str, str, str]] = [] + self.periods: List[str] = [] + self.currencies: List[str] = [] + self.enums: OrderedDict = OrderedDict() + self.entities: List[Tuple[str, Dict]] = [] + + def add_table(self, t: str): + if t and t not in self.tables: + self.tables.append(t) + if t not in self.fields_by_table: + self.fields_by_table[t] = [] + + def add_field(self, tbl: str, fld: str): + if tbl and fld: + self.add_table(tbl) + fq = f"{tbl}.{fld}" + if fq not in self.fields_by_table[tbl]: + self.fields_by_table[tbl].append(fq) + + def add_join(self, on: str): + if on and on not in self.joins: + self.joins.append(on) + + def add_metric(self, mname: str, mexpr: str, mtbl: str): + if mtbl: + self.add_table(mtbl) + if mname: + tup = (mname, mexpr or "", mtbl or "") + if tup not in self.metrics: + self.metrics.append(tup) + + def add_period(self, pname: str): + if pname and pname not in self.periods: + self.periods.append(pname) + + def add_currency(self, code: str): + if code and code not in self.currencies: + self.currencies.append(code) + + def add_enum(self, tbl: str, fld: str, vals: Any): + if tbl: + self.add_table(tbl) + if tbl and fld: + key = f"{tbl}.{fld}" + if isinstance(vals, (list, tuple)): + vals = ", ".join([str(v) for v in vals]) + if key not in self.enums: + self.enums[key] = vals or "" + self.add_field(tbl, fld) + + def add_entity(self, ent_name: str, filt: Dict): + self.entities.append((ent_name, filt or {})) + + def sort(self): + self.tables.sort() + for t in self.fields_by_table: + if t not in self.tables: + self.tables.append(t) + for t in self.fields_by_table: + self.fields_by_table[t] = sorted( + self.fields_by_table[t], key=lambda s: s.split(".", 1)[1] + ) + self.joins.sort() + self.metrics.sort(key=lambda x: x[0]) + self.periods.sort() + self.currencies.sort() + self.enums = OrderedDict(sorted(self.enums.items(), key=lambda kv: kv[0])) + +def _process_enum(txt: str, md: Dict, acc: _SchemaAccumulator): + tbl = md.get("table") or _parse_tag(txt, "TABLE") + fld = md.get("field") + if not fld: + ef = _parse_tag(txt, "ENUM") + if "." in ef: + tbl = tbl or ef.split(".", 1)[0].strip() + fld = ef.split(".", 1)[1].strip() + vals = md.get("values") + if vals is None: + vals = _parse_tag(txt, "VALUES") + acc.add_enum(tbl, fld, vals) + +def _process_entity(txt: str, md: Dict, acc: _SchemaAccumulator): + ent_name = md.get("entity") or _parse_tag(txt, "ENTITY") or "Entity" + filt = md.get("filters") + if filt is None: + filt_txt = _parse_tag(txt, "FILTERS") + filt = {} + if filt_txt: + for part in [p.strip() for p in filt_txt.split(";") if p.strip()]: + if "=" in part: + k, v = part.split("=", 1) + filt[k.strip()] = [x.strip() for x in v.split(",") if x.strip()] + acc.add_entity(ent_name, filt) + +def _get_table_name(txt: str, md: Dict) -> str: + return md.get("table") or _parse_tag(txt, "TABLE") + +def _get_field_name(txt: str, md: Dict) -> str: + return md.get("field") or _parse_tag(txt, "FIELD").split(" (", 1)[0] + +def _process_table_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + acc.add_table(_get_table_name(txt, md)) + +def _process_field_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + acc.add_field(_get_table_name(txt, md), _get_field_name(txt, md)) + +def _process_join_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + acc.add_join(md.get("on") or _parse_tag(txt, "ON")) + +def _process_metric_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + acc.add_metric( + md.get("name") or _parse_tag(txt, "METRIC"), + md.get("expression") or _parse_tag(txt, "EXPR"), + _get_table_name(txt, md), + ) + +def _process_period_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + acc.add_period(md.get("name") or _parse_tag(txt, "PERIOD")) + +def _process_currency_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + acc.add_currency(md.get("code") or _parse_tag(txt, "CURRENCY")) + +_DOC_PROCESSORS = { + "table": _process_table_doc, + "field": _process_field_doc, + "join": _process_join_doc, + "metric": _process_metric_doc, + "period": _process_period_doc, + "currency": _process_currency_doc, + "enum": _process_enum, + "entity": _process_entity, +} + +def _process_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: + dtype = md.get("type") or _infer_type(txt) + processor = _DOC_PROCESSORS.get(dtype) + if processor: + processor(txt, md, acc) + + +def _append_table_lines( + lines: List[str], + acc: _SchemaAccumulator, + max_fields_per_table: int, +) -> None: + for tbl in acc.tables: + lines.append(f"Table: {tbl}") + lines.append("Fields:") + fields = acc.fields_by_table.get(tbl, []) + if not fields: + lines.append(" -") + lines.append("") + continue + lines.extend(f" - {field}" for field in fields[:max_fields_per_table]) + extra_count = len(fields) - max_fields_per_table + if extra_count > 0: + lines.append(f" # +{extra_count} more") + lines.append("") + + +def _append_simple_section(lines: List[str], title: str, items: List[str]) -> None: + if not items: + return + lines.append(f"{title}:") + lines.extend(f" - {item}" for item in items) + lines.append("") + + +def _append_metric_lines(lines: List[str], acc: _SchemaAccumulator) -> None: + if not acc.metrics: + return + lines.append("Metrics:") + for metric_name, metric_expr, metric_table in acc.metrics: + suffix = f" # table: {metric_table}" if metric_table else "" + line = f" - {metric_name}: {metric_expr}{suffix}" if metric_expr else f" - {metric_name}{suffix}" + lines.append(line) + lines.append("") + +def _append_enum_lines(lines: List[str], acc: _SchemaAccumulator) -> None: + if not acc.enums: + return + lines.append("Enums:") + for key, values in acc.enums.items(): + lines.append(f" - {key}: {values}" if values else f" - {key}") + lines.append("") + +def _format_filter_value(value: Any) -> str: + if isinstance(value, (list, tuple)): + return ", ".join(str(item) for item in value) + return str(value) + +def _append_entity_lines( + lines: List[str], + acc: _SchemaAccumulator, + show_entity_filters_yaml: bool, +) -> None: + if not acc.entities: + return + lines.append("Entities:") + for entity, filters in acc.entities: + if show_entity_filters_yaml and isinstance(filters, dict) and filters: + lines.append(f" - Entity: {entity}") + lines.append(" Filters:") + for key, value in filters.items(): + lines.append(f" {key}: {_format_filter_value(value)}") + continue + + lines.append(f" - Entity: {entity}, Filters: {filters if filters else '{}'}") + +def _trim_trailing_blank_lines(lines: List[str]) -> None: + while lines and not lines[-1].strip(): + lines.pop() + +def _build_context_lines( + acc: _SchemaAccumulator, + title: str, + max_fields_per_table: int, + show_entity_filters_yaml: bool, +) -> List[str]: + lines: List[str] = [title] + + _append_table_lines(lines, acc, max_fields_per_table) + + if acc.joins: + lines.append("Join:") + lines.extend(f" {join}" for join in acc.joins) + lines.append("") + + _append_metric_lines(lines, acc) + _append_simple_section(lines, "Periods", acc.periods) + _append_simple_section(lines, "Currencies", acc.currencies) + _append_enum_lines(lines, acc) + _append_entity_lines(lines, acc, show_entity_filters_yaml) + + _trim_trailing_blank_lines(lines) + return lines + +def hits_to_schema_context( + hits: Union[List[Any], Dict, str], + title: str = "SCHEMA CONTEXT", + max_fields_per_table: int = 20, + sort_sections: bool = True, + show_entity_filters_yaml: bool = True +) -> str: + acc = _SchemaAccumulator() + for txt, md in _collect_docs(hits): + _process_doc(txt, md, acc) + if sort_sections: + acc.sort() + lines = _build_context_lines(acc, title, max_fields_per_table, show_entity_filters_yaml) + return "\n".join(lines) + + diff --git a/changai/changai/api/v2/store_chats.py b/changai/changai/api/v2/store_chats.py index dc578e0..73aefe5 100644 --- a/changai/changai/api/v2/store_chats.py +++ b/changai/changai/api/v2/store_chats.py @@ -1,8 +1,49 @@ import json import frappe -from typing import Any +from typing import Any, Optional, Dict +from rapidfuzz import fuzz CHANGAI_CHAT_HIST_DOC = "ChangAI Chat History" - + +def save_logs( + user_question: Optional[str] = None, + formatted_q: Optional[str] = None, + context: Optional[str] = None, + sql: Optional[str] = None, + val: Any = None, + result: Any = None, + tries: Optional[int] = None, + err: Any = None, + formatted_result: Any = None, + tables:Any=None, + fields:Any=None, + entity_debug:Any=None, + type_=None +) -> str: + def to_json_if_needed(v: Any) -> Any: + if isinstance(v, (dict, list)): + return json.dumps(v, default=str, ensure_ascii=False) + return v + + MAX_LOG_LEN = 140 + doc = frappe.new_doc("ChangAI Logs") + doc.user_question = user_question + safe_question=(formatted_q[:137] + "..." if len(formatted_q) > MAX_LOG_LEN else formatted_q) + doc.rewritten_question = safe_question + doc.schema_retrieved = to_json_if_needed(context) + doc.sql_generated = to_json_if_needed(sql) + doc.validation = to_json_if_needed(val) + doc.tries = tries + doc.error = to_json_if_needed(err) + doc.result = to_json_if_needed(result) + doc.formatted_result = to_json_if_needed(formatted_result) + doc.tables = to_json_if_needed(tables) + doc.fields = to_json_if_needed(fields) + doc.entity = to_json_if_needed(entity_debug) + doc.type = type_ + doc.insert(ignore_permissions=True) + return doc.name + + def save_message_doc(session_id:str,message_type:str,content:str): doc=frappe.get_doc({ @@ -153,6 +194,101 @@ def respond_from_cache(user_question:str): User Question: {qstn}""" + +def find_similar_log_question(new_question:str, threshold: int = 90): + logs = frappe.get_all( + "ChangAI Logs", + fields=["name", "user_question", "sql_generated","rewritten_question","fields","tables","error","entity","result","type"], + limit_page_length=500 + ) + best_match = None + best_score = 0 + for log in logs: + score = fuzz.token_set_ratio(new_question, log.rewritten_question) + if score > best_score: + best_score = score + best_match = log + + if best_score >= threshold: + return { + "matched": True, + "score": best_score, + "log_name": best_match.name, + "question": best_match.user_question, + "sql": best_match.sql_generated, + "rewritten_question":best_match.rewritten_question, + "fields":best_match.fields, + "tables":best_match.tables, + "entity_debug":best_match.entity, + "result":best_match.result, + "error":best_match.error, + "type":best_match.type + } + + return { + "matched": False, + "score": best_score + } + +def _error_response(memory_status, user_question, formatted_q, context, + selected_tables, fields, sql, validation, + entity_debug, tries, error, err): + return { + "Memory Status": memory_status, + "Question": user_question, + "Formatted_Question": formatted_q, + "Context": (context or "")[:800], + "Tables": selected_tables, + "Fields": fields, + "SQL": sql, + "Validation": validation, + "EntityDebug": entity_debug, + "Tries": tries, + "Error": error, + "Result": [], + "Bot": _get_sql_error_message(error, validation), + } + +def _get_sql_error_message(err: Any, val: Dict) -> str: + # if err: + # frappe.log_error(err, "ChangAI SQL Pipeline Error") + # return "⚠️ The model encountered an error generating your query. Please try the same Question again." + + error_text = (val.get("error") or "").strip() + + if not error_text: + return "⚠️ Could nprocess your request. Please try rephrasing." + + if "Empty SQL from LLM" in error_text: + return "⚠️ The model could not generate a SQL query for your question. Please try rephrasing." + + if "does not exist in schema" in error_text: + return f"⚠️ The model generated an invalid table reference. {error_text}" + + if "could not be resolved" in error_text: + return f"⚠️ The model generated an invalid field reference. {error_text}" + + if "parse" in error_text.lower() or "syntax" in error_text.lower() or "expected" in error_text.lower(): + return "⚠️ The model generated invalid SQL syntax. Please try rephrasing." + + return f"⚠️ The model generated an invalid query. {error_text}" + +def get_last_thread_message(chat_id: str): + data = frappe.get_all( + "ChangAI Chat History", + filters={"session_id": chat_id}, + fields=["content"], + order_by="creation asc" + ) + for row in reversed(data): + try: + msg = json.loads(row["content"]) + # human_msg = msg[-2]["human"] + msg_type = msg[-2]["type"] + return msg_type + except Exception: + pass + return "" @frappe.whitelist(allow_guest=False) def inject_prompt(user_qstn: str, session_id: str) -> str: rows=get_chat_history(session_id) @@ -168,4 +304,4 @@ def normalize(value): return value if isinstance(value, (list, dict)): return json.dumps(value) - return value \ No newline at end of file + return value diff --git a/changai/changai/api/v2/text2sql_pipeline_v2.py b/changai/changai/api/v2/text2sql_pipeline_v2.py index 6513ddd..d366ddb 100644 --- a/changai/changai/api/v2/text2sql_pipeline_v2.py +++ b/changai/changai/api/v2/text2sql_pipeline_v2.py @@ -1,641 +1,90 @@ from langgraph.graph import StateGraph, END -from collections import OrderedDict from typing_extensions import TypedDict -from typing import Any, Dict, List, Tuple, Union, Optional, Set -import boto3 +from typing import Any, Dict, List, Tuple, Optional import requests import json -from changai.changai.api.v2.non_erp_handler import handle_non_erp_query -import yaml -import re -from frappe.utils.jinja import render_template -import os -import pickle -import numpy as np -import time import base64 -import sqlglot -from functools import lru_cache -from sqlglot import exp +import os +from frappe.utils.jinja import render_template +from changai.changai.api.v2.schema_utils import match_report_intent, get_report_filter_fields +from changai.changai.api.v2.store_chats import get_last_thread_message +from changai.changai.api.v2.retrieve import ( + call_retrieve_multi_line, + call_fvs_table_search, + call_entity_retriever, + check_memory_status, + remote_entity_embedder, + get_embedding_engine, + get_vs, + load_field_matrix, + get_master_vs +) +import re +from changai.changai.api.v2.tts import get_polly_client from rapidfuzz import fuzz, process from langgraph.checkpoint.memory import MemorySaver -from langchain_community.vectorstores import FAISS -from langchain_huggingface import HuggingFaceEmbeddings -from google import genai -from google.genai import types -from changai.changai.api.v2.schema_utils import validate_sql_schema,_load_mapping_data,check_file_updates -from google.oauth2 import service_account -import threading +from changai.changai.api.v2.schema_utils import ( + validate_sql_schema, + check_file_updates, + read_asset, + clean_sql, + validate_sql_against_mapping, + hits_to_schema_context, + CHANGAI_GUIDE_LINK, + ERPGULF_LINK, + settingsUrl, + publish_pipeline_update, + ChangAIConfig +) from werkzeug.wrappers import Response from changai.changai.api.v2.helpdesk_api import( create_helpdesk_ticket, get_user_tickets ) -from typing import Any, Dict, Optional -import jinja2 import frappe -from google.api_core import exceptions as google_exceptions from changai.changai.api.v2.store_chats import ( save_turn_2, inject_prompt, + save_logs, + find_similar_log_question, + _get_sql_error_message, + _error_response ) -# from changai.changai.api.v2.non_erp_handler import IntelligentStaticResponder -from huggingface_hub import snapshot_download +from changai.changai.api.v2.format_output import ( + format_data_conversationally, + format_data + +) +from changai.changai.api.v2.clients import call_model,gemini_client +from changai.changai.api.v2.non_erp_handler import non_erp_response from frappe.desk.reportview import build_match_conditions -import shutil from frappe import _ -from pathlib import Path -import numpy as np -from typing import List, Dict, Any -# from changai.changai.api.v2.embedding_client import LocalEmbeddingService -# from changai.changai.api.v2.embedding_client import get_local_embedding -# from symspellpy.symspellpy import SymSpell -sym_spell = None -_GEMINI_CLIENT = None -_GEMINI_CONFIG = None -_FIELD_DOCS_CACHE = None -_FIELD_EMBS_CACHE = None -_TABLE_TO_IDX_CACHE = None -_VS_REPORT=None -_KEYWORDS_SET=None -_KEYWORDS_LIST=None -ERPGULF_LINK = "https://app.erpgulf.com/en/products/chang-ai-an-ai-agent" -_ASSETS_DIR = Path(frappe.get_app_path("changai", "changai", "api", "v2", "assets")).resolve() -_PROMPTS_DIR = Path(frappe.get_app_path("changai", "changai", "prompts")).resolve() -CHANGAI_SETTINGS = "ChangAI Settings" -_ALLOWED_EXT = {".json", ".yaml",".j2", ".yml", ".txt", ".md"} -# _warmup_done=False -# def warm_up(): -# global _warmup_done -# try: -# import time -# time.sleep(3) # wait for frappe to fully initialize first -# frappe.logger().info("ChangAI: background warmup starting...") -# load_on_startup() -# _warmup_done = True -# frappe.logger().info("ChangAI: background warmup complete ✅") -# except Exception as e: -# frappe.logger().error(f"ChangAI: background warmup failed: {e}") - - -SQL_REWRITE_PROMPT = """You are an ERP query rewriter and entity detector. -Return ONLY valid JSON: -{{"standalone_question":"...","contains_values":true/false,"entity_word":["..."]}} - -TASK 1 — FOLLOW-UP -- If the query depends on previous messages, rewrite it as a complete standalone question. -- Otherwise keep it unchanged. - -TASK 2 — ENTITY DETECTION -contains_values = TRUE: Any noun that refers to a specific named master record -(item name, customer name, supplier name, warehouse name, employee name) -If not sure, also set contains_values = TRUE, otherwise contains_values = FALSE. -if contains_values = TRUE, then entity_word = the most relevant noun/entity name from the question and place it here and return only if contains_values is TRUE. -if multiple entites are present,place all of them in entity_word list. -TASK 3 — ERP CONTEXTUAL REWRITE - -1. Normalize: -- Fix typos, clear English -- Do NOT change entity values - -2. Complete intent: -- Never change the question's intent — only fix grammar and map ERP terms. - -3. ERP mapping: -- Map generic terms to standard ERPNext concepts based on intent -- Avoid vague words if clearer business terms exist -- Do NOT invent documents or use report names. -Examples: - stock → Bin / Stock Ledger Entry - production → Work Order - finance/profit → GL Entry - -4. Field hints (max 1–2): -Use natural phrasing ("based on", "using"): - sales → grand_total - qty → qty - stock → actual_qty - production → produced_qty - finance → debit / credit - status → status - -5. Time fields: - Sales/Stock/Finance → posting_date - Work Order → actual_start_date / actual_end_date - Timesheet → start_date / end_date - Timesheet Detail → from_time / to_time -- NEVER use posting_date for Timesheet -- NEVER use creation unless asked - -6. Relationships: -- Include linked entities if required - -STYLE: -- Natural business language -- No SQL, no tab* names - -EXAMPLES: -"total sales amount last month" -→ What is the total sales amount from Sales Invoices last month based on grand_total and posting_date? - -"stock in warehouse a" -→ What is the stock quantity in Warehouse A based on actual_qty from Bin? - -"who worked today" -→ Which employees logged time today based on Timesheet start_date or Timesheet Detail from_time? - -STRICT RULES: -- If the query mentions Draft, Submitted, or Cancelled, explicitly include docstatus in the rewritten question. -- Do not add a specific document type unless clearly implied by the user query or required by standard ERPNext business meaning. -- For vague money questions, clarify the business meaning as actual, ordered, quoted, paid, or outstanding — do not guess the document type incorrectly. -- If the user says "spend", treat it as actual purchase/expense, not quotation or order commitment, unless the user explicitly mentions order, quotation, or planned purchase. -- Preserve all filter conditions, status values, and keywords from the original question — never drop them during rewriting. -- Do NOT add dates, filters, entities, statuses, or assumptions unless explicitly present in the user question or clearly inferred from conversation memory. -- Use chat history only when the current query clearly implies continuation or follow-up context. Never assume dates, filters, entities, or conditions from previous messages unless strongly indicated. -- Use only the most relevant tables and fields required for the user query. -- Use only valid tables and fields from the provided schema context, regardless of retrieval ranking order. -- Choose fields based on business meaning and user intent, not rank position. -- Never invent schema elements. -- Always return any one clear user-readable business field, not only technical IDs, unless explicitly requested. -- If the query is ambiguous, ask for clarification and set "clarify": true.""" -# def get_symspell(): -# global sym_spell - -# if sym_spell is not None: -# frappe.logger().info(f"SymSpell already loaded, skipping PID: {os.getpid()}") -# return sym_spell - -# frappe.logger().error(f"SymSpell loading NOW in PID: {os.getpid()}") - -# sym_spell = SymSpell(max_dictionary_edit_distance=4, prefix_length=7) - -# dictionary_path = frappe.get_app_path( -# "changai", -# "changai", -# "api", -# "v2", -# "assets", -# "frequency_dictionary_en_82_765.txt" -# ) - -# sym_spell.load_dictionary(dictionary_path, term_index=0, count_index=1) - -# for kw in BUSINESS_KEYWORDS: -# sym_spell.create_dictionary_entry(kw.lower(), 1000) - -# return sym_spell - -@lru_cache(maxsize=512) -def is_child_table(table: str) -> bool: - doctype = table.replace("tab", "", 1) if table.startswith("tab") else table - - try: - meta = frappe.get_meta(doctype, cached=True) - return bool(getattr(meta, "istable", 0)) - except Exception: - return False - -CHILD_GENERIC_FIELDS = ["parent", "parenttype", "parentfield", "idx"] -MAIN_GENERIC_FIELDS = ["name", "docstatus"] -def enrich_fields_for_sql_context(table: str, fields: list[str]) -> list[str]: - out = list(fields) - - if is_child_table(table): - for f in reversed(CHILD_GENERIC_FIELDS): - if f not in out: - out.insert(0, f) - else: - for f in reversed(MAIN_GENERIC_FIELDS): - if f not in out: - out.insert(0, f) - - return out - - -def format_schema_context(grouped: dict) -> str: - parts = [] - - for table, table_data in grouped.items(): - if isinstance(table_data, dict): - raw_fields = table_data.get("fields", []) - is_table_value = table_data.get("is_table") - - if is_table_value is None: - child = is_child_table(table) - else: - child = bool(is_table_value) - else: - raw_fields = table_data - child = is_child_table(table) - - fields = enrich_fields_for_sql_context(table, raw_fields) - - parts.append(f"TABLE: {table}") - parts.append(f"TYPE: {'Child Table' if child else 'Main Table'}") - - if child: - parts.append("JOIN RULES:") - parts.append("- parent = parent document name") - parts.append("- parenttype = parent DocType") - parts.append("- parentfield = child table fieldname") - - parts.append("FIELDS:") - for field in fields: - parts.append(f"- {field}") - - parts.append("") - - return "\n".join(parts) - - -def publish_pipeline_update(request_id, stage, message, data=None, done=False, error=False): - if not request_id: - return - payload = { - "request_id": request_id, - "stage": stage, - "message": message, - "data": data or {}, - "done": done, - "error": error, - "timestamp": frappe.utils.now_datetime().isoformat(), - } - frappe.publish_realtime( - event=f"debug_{request_id}", - message=payload, - user=frappe.session.user, - ) - - -def _safe_join(base: Path, rel: str) -> Path: - """ - Prevent path traversal. Only allow reading inside base directory. - """ - p = (base / rel).resolve() - if base != p and base not in p.parents: - frappe.throw(_("Unsafe path: {0}\n" - "Check Quick Start Guide Here 👇:\n {1}").format(rel,CHANGAI_GUIDE_LINK)) - return p - - -def read_asset(file_name: str, base: str = "assets") -> Any: - """ - base: - - "assets" -> changai/changai/api/v2/assets - - "prompts" -> changai/changai/prompts - """ - file_name = (file_name or "").strip() - if not file_name: - frappe.throw(_("file_name is required\n" - "Check Quick Start Guide Here 👇:\n {0}").format(CHANGAI_GUIDE_LINK)) - - ext = Path(file_name).suffix.lower() - if ext not in _ALLOWED_EXT: - frappe.throw(_("Unsupported file type: {0}\n" - "Check Quick Start Guide Here 👇:\n {1}").format(ext, CHANGAI_GUIDE_LINK)) - - if base == "assets": - root = _ASSETS_DIR - elif base == "prompts": - root = _PROMPTS_DIR - else: - root = None - if root is None: - frappe.throw(_("Invalid base: {0}\n" - "Check Quick Start Guide Here 👇:\n {1}").format(base, CHANGAI_GUIDE_LINK)) - # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal - path = _safe_join(root, file_name) - - if not path.is_file(): - frappe.throw(_("File not found: {0}\n" - "Check Quick Start Guide Here 👇:\n {1}").format(str(path), CHANGAI_GUIDE_LINK)) - - content = path.read_text(encoding="utf-8", errors="replace") - - if ext == ".json": - try: - return json.loads(content) - except json.JSONDecodeError as e: - frappe.throw(_("Invalid JSON in {0}: {1}" - "Check Quick Start Guide Here 👇:\n {2}").format(str(path), str(e), CHANGAI_GUIDE_LINK)) - if ext == ".yaml" or ext == ".yml": - try: - return yaml.safe_load(content) - except yaml.YAMLError as e: - frappe.throw(_("Invalid YAML in {0}: {1}" - "Check Quick Start Guide Here 👇:\n {2}").format(str(path), str(e), CHANGAI_GUIDE_LINK)) - return content - -_VS_TABLE = None -_VS_MASTER = None -_EMBEDDER_INSTANCE = None -_FULL_FIELDS_VS = None +from frappe.desk.query_report import get_script +from changai.changai.api.v2.clients import ( + call_model, + call_gemini, + remote_embedder_request, +) STATUS_200 = 200 -_SUB_VS_CACHE = {} APPLICATION_JSON = "application/json" -CHANGAI_GUIDE_LINK="https://app.erpgulf.com/en/articles/chang-ai-quick-start-guide" EMBEDDING_ENGINE_NONE_MESSG = f""" Embedding engine is None. Model not loaded. Check Quick Start Guide Here 👇: {CHANGAI_GUIDE_LINK}""" -MODEL_ID = "gemini-2.5-flash-lite" +REPORT_ACTION_PROMPT = read_asset("report_prompt.txt",base="prompts") RETRY_LIMIT = 2 -REPORT_INTENT_MAP = read_asset("report_intent_map.json",base="assets") bk = read_asset("business_keywords_v1.json", base="assets") BUSINESS_KEYWORDS = bk.get("business_keywords", bk) - mapping_data = read_asset("metaschema_clean_v2.json", base="assets") CONVERSATION_TEMPLATE = read_asset("conversation_template_v2.j2", base="assets") SQL_SYS_PROMPT = read_asset("sql_system_prompt.txt", base="prompts") SQL_PROMPT = read_asset("sql_user_prompt.txt", base="prompts") -FORMAT_PROMPT = read_asset("user_friendly_prompt.txt", base="prompts") -NON_ERP_PROMPT = read_asset("non_erp_prompt.txt", base="prompts") -SUPPORT_PROMPT = read_asset("support.txt", base="prompts") -SUPPORT_USER_PROMPT = read_asset("support_user_prompt.txt", base="prompts") -SUPPORT_SYS_PROMPT = read_asset("support_sys_prompt.txt", base="prompts") - FILTER_TABLES = read_asset("filter_tables.txt", base="prompts") filter_fields = read_asset("filter_fields.txt", base="prompts") - -@frappe.whitelist(allow_guest=False) -def download_model(): - frappe.enqueue( - "changai.changai.api.v2.text2sql_pipeline_v2.download_model_from_ui", # dot-path to the function - queue="long", # use "long" queue for heavy tasks - timeout=3600, # 1 hour timeout (in seconds) - is_async=True, # run in background (default True) - job_name="download_model", # optional: helps track/deduplicate jobs - ) - return { - "ok":True,"message":"Model Downloading.." - } - -def _get_model_path(): - site_path = frappe.get_site_path("private", "files", "changai_model") - return site_path - - -@frappe.whitelist(allow_guest=False) -def download_model_from_ui(): - global _EMBEDDER_INSTANCE - - model_path = _get_model_path() - - try: - if os.path.exists(model_path): - shutil.rmtree(model_path) - - os.makedirs(model_path, exist_ok=True) - - snapshot_download( - repo_id="hyrinmansoor/changAI-nomic-embed-text-v1.5-finetuned", - local_dir=model_path, - ignore_patterns=[ - "*.pt", - "*.pth", - "*.bin", - "trainer_*", - "optimizer*" - ] - ) - _EMBEDDER_INSTANCE = None - return {"status": "success", "message": "Embedding model downloaded successfully."} - - except Exception as e: - frappe.log_error(frappe.get_traceback(), "Embedding Model Download Failed") - frappe.throw(_("Model download failed: {0}\n Check Quick Start Guide Here 👇:\n{1}
" - "Download Embedding Model.
" - "ERPGulf.com." -).format(str(e),CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK)) - - -_FIELD_DOCS_CACHE = None -_FIELD_EMBS_CACHE = None -_TABLE_TO_IDX_CACHE = None - - -def load_field_matrix(): - global _FIELD_DOCS_CACHE, _FIELD_EMBS_CACHE, _TABLE_TO_IDX_CACHE - - if _FIELD_DOCS_CACHE is not None: - return _FIELD_DOCS_CACHE, _FIELD_EMBS_CACHE, _TABLE_TO_IDX_CACHE - - app_root = Path(frappe.get_app_path("changai")).resolve() - schema_rel = "changai/api/v2/fvs_stores/erpnext/emb_dir" - # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal - schema_path = _safe_join(app_root, schema_rel) - - embs_path = schema_path / "field_embs.npy" - docs_path = schema_path / "field_docs.pkl" - table_idx_path = schema_path / "table_to_idx.pkl" - - if not embs_path.exists(): - frappe.throw(f"Missing field_embs.npy. Rebuild schema FVS first: {embs_path}") - - # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal - with open(docs_path, "rb") as f: - docs = pickle.load(f) - - # nosemgrep: frappe-semgrep-rules.rules.security.frappe-security-file-traversal - with open(table_idx_path, "rb") as f: - table_to_idx = pickle.load(f) - - embs = np.load(embs_path, mmap_mode="r") - - _FIELD_DOCS_CACHE = docs - _FIELD_EMBS_CACHE = embs - _TABLE_TO_IDX_CACHE = table_to_idx - - return docs, embs, table_to_idx - - -def get_embedding_engine(): - global _EMBEDDER_INSTANCE - if _EMBEDDER_INSTANCE is not None: - return _EMBEDDER_INSTANCE - - model_path = _get_model_path() # check path first, always - - if not os.path.exists(model_path): - _EMBEDDER_INSTANCE = None # reset if model missing - frappe.throw( - _( - "Go to ChangAI Settings and click" - "Download Embedding Model.

" - "Check this Quick Start Guide for more detail: " - "Click here" - "ERPGulf.com." - - ).format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), - title=_("Embedding Model Required") - ) - - if _EMBEDDER_INSTANCE is None: - _EMBEDDER_INSTANCE = HuggingFaceEmbeddings( - model_name=model_path, - model_kwargs={"device": "cpu","trust_remote_code": True,}, - encode_kwargs={ - "normalize_embeddings": True, - }, - ) - - return _EMBEDDER_INSTANCE - -def _build_frontend_settings_config() -> Dict[str, Any]: - settings = frappe.get_single(CHANGAI_SETTINGS) - - aws_access_key_id = (getattr(settings, "aws_access_key_id", None) or "").strip() - aws_secret_access_key = (getattr(settings, "aws_secret_access_key", None) or "").strip() - aws_region = ( - getattr(settings, "aws_region", None) - or getattr(settings, "aws_default_region", None) - or "us-east-1" - ) - - return { - "RETAIN_MEM": settings.retain_memory, - "LLM_VERSION_ID": settings.llm_version_id, - "EMBED_VERSION_ID": settings.embedder_version_id, - "REMOTE": bool(settings.remote), - "deploy_url": settings.deploy_url, - "entity_retriever": settings.entity_retriever, - "support_api_url": settings.support_url, - "get_ticket_details_url": settings.get_ticket_details_url, - "llm": settings.llm, - "location": settings.gemini_location, - "retriever_structure": settings.retriever_structure, - "gemini_project_id": settings.gemini_project_id, - "gemini_json_content": settings.gemini_json_content, - "enable_voice_chat": bool(settings.enable_voice_chat), - "aws_region": aws_region, - "polly_voice_id": "Zayd", - "polly_enabled": bool(settings.enable_voice_chat and aws_access_key_id and aws_secret_access_key), - "enable_changai": bool(settings.enable_changai) - } - - -@frappe.whitelist(allow_guest=False) -def get_settings() -> Dict[str, Any]: - settings = frappe.get_single(CHANGAI_SETTINGS) - config = { - "RETAIN_MEM": settings.retain_memory, - "LLM_VERSION_ID": settings.llm_version_id, - "EMBED_VERSION_ID": settings.embedder_version_id, - "API_TOKEN": settings.api_token, - "REMOTE": bool(settings.remote), - "deploy_url": settings.deploy_url, - "entity_retriever": settings.entity_retriever, - "support_api_url": settings.support_url, - "get_ticket_details_url": settings.get_ticket_details_url, - "llm": settings.llm, - "location": settings.gemini_location, - "retriever_structure": settings.retriever_structure, - "gemini_project_id": settings.gemini_project_id, - "gemini_json_content": settings.gemini_json_content, - "aws_access_key_id": settings.aws_access_key_id, - "aws_secret_access_key": settings.aws_secret_access_key, - "enable_voice_chat": settings.enable_voice_chat, - } - return config - - -@frappe.whitelist(allow_guest=False) -def get_frontend_settings() -> Dict[str, Any]: - return _build_frontend_settings_config() - -class ChangAIConfig: - @classmethod - def get(cls): - if not hasattr(frappe.local, "_changai_config"): - frappe.clear_document_cache(CHANGAI_SETTINGS) - frappe.local._changai_config = get_settings() - return frappe.local._changai_config -_POLLY_CLIENT = None - -def get_polly_client(config): - global _POLLY_CLIENT - - if _POLLY_CLIENT is None: - _POLLY_CLIENT = boto3.client( - "polly", - aws_access_key_id=(config.get("aws_access_key_id") or "").strip(), - aws_secret_access_key=(config.get("aws_secret_access_key") or "").strip(), - region_name=(config.get("aws_region") or "us-east-1"), - ) - return _POLLY_CLIENT - -def build_ssml(text: str) -> str: - parts = [] - current = [] - current_lang = None - - for token in text.split(): - lang = "ar-AE" if re.search(r'[\u0600-\u06FF]', token) else "en-US" - - if current_lang is None: - current_lang = lang - - if lang != current_lang: - parts.append( - f'{" ".join(current)}' - ) - current = [token] - current_lang = lang - else: - current.append(token) - - if current: - parts.append( - f'{" ".join(current)}' - ) - - return "" + " ".join(parts) + "" -@frappe.whitelist(allow_guest=False) -def synthesize_tts(text: str, voice_id: Optional[str] = None) -> Dict[str, Any]: - config = ChangAIConfig.get() - if not bool(config.get("enable_voice_chat")): - return {"ok": False, "error": "Voice chat is disabled in settings.", "provider": "browser"} - aws_access_key_id = (config.get("aws_access_key_id") or "").strip() - aws_secret_access_key = (config.get("aws_secret_access_key") or "").strip() - if not aws_access_key_id or not aws_secret_access_key: - return {"ok": False, "error": "AWS Polly credentials are missing.", "provider": "browser"} - cleaned_text = re.sub(r"<[^>]*>", " ", text or "") - cleaned_text = re.sub(r"\s+", " ", cleaned_text).strip() - if not cleaned_text: - return {"ok": False, "error": "Text is empty.", "provider": "browser"} - - if len(cleaned_text) > 2500: - cleaned_text = cleaned_text[:2500] - - try: - polly_client = get_polly_client(config) - voice = (voice_id or config.get("polly_voice_id") or "Zayd").strip() or "Zayd" - ssml_text = build_ssml(cleaned_text) - response = polly_client.synthesize_speech( - Text=ssml_text, - OutputFormat="mp3", - VoiceId="Zayd", - Engine="neural", - TextType="ssml", -) - stream = response.get("AudioStream") - if stream is None: - return {"ok": False, "error": "Polly did not return audio stream.", "provider": "browser"} - - audio_bytes = stream.read() - audio_base64 = base64.b64encode(audio_bytes).decode("utf-8") - return { - "ok": True, - "provider": "polly", - "mime_type": "audio/mpeg", - "audio_base64": audio_base64, - "voice_id": voice, - } - except Exception as e: - frappe.log_error(frappe.get_traceback(), "ChangAI Polly TTS Error") - return {"ok": False, "error": str(e), "provider": "browser"} - - +STOP_WORDS = set(read_asset("stop_words.json", base="assets")) +THREAD_WORDS = set(read_asset("thread_words.json", base="assets")) +SQL_REWRITE_SYS_PROMPT = read_asset("sql_rewrite_sys_prompt.txt", base="prompts") +SQL_REWRITE_USER_PROMPT = read_asset("sql_rewrite_user_prompt.txt", base="prompts") @frappe.whitelist(allow_guest=True) # nosemgrep: security.guest-whitelisted-method - intentional, validates credentials via OAuth client lookup and Frappe password grant before returning a token def generate_token_secure(api_key: str, api_secret: str, app_key: str): try: @@ -649,7 +98,6 @@ def generate_token_secure(api_key: str, api_secret: str, app_key: str): status=401, mimetype=APPLICATION_JSON, ) - doc = frappe.db.get_value( "OAuth Client", {"app_name": app_key}, @@ -696,7 +144,6 @@ def generate_token_secure(api_key: str, api_secret: str, app_key: str): mimetype=APPLICATION_JSON, ) - # Api for checking user name using token @frappe.whitelist(allow_guest=False) def whoami() -> Dict[str, Any]: @@ -716,7 +163,6 @@ def whoami() -> Dict[str, Any]: ) except ValueError as ve: frappe.throw(_("{0}\n Check Quick Start Guide Here 👇:\n {1}").format(str(ve),CHANGAI_GUIDE_LINK)) - def extract_tables_from_sql(sql: str) -> List[str]: @@ -732,290 +178,6 @@ def extract_tables_from_sql(sql: str) -> List[str]: tables.append(t) return tables - -def call_model(prompt: str, task: str = "llm",sys_prompt: str = "") -> Any: - config = ChangAIConfig.get() - if config["REMOTE"] and config["llm"] == "QWEN3": - return remote_llm_request_deploy_test(prompt=prompt, task=task) - else: - if config["llm"] == "Gemini": - return call_gemini(prompt,sys_prompt) - - -def _post_json(url: str, headers: Dict[str, str], payload: Dict[str, Any], timeout: int = 120): - try: - res = requests.post(url, headers=headers, json=payload, timeout=timeout) - ct = (res.headers.get("Content-Type") or "").lower() - try: - body = res.json() if APPLICATION_JSON in ct else {"raw_text": res.text} - except Exception: - body = {"raw_text": res.text} - if res.status_code not in (STATUS_200, 201, 202): - return {"ok": False, "status_code": res.status_code, "body": body} - return {"ok": True, "status_code": res.status_code, "body": body} - except requests.exceptions.Timeout: - return {"ok": False, "status_code": None, "body": {"error": "timeout"}} - except Exception as e: - return {"ok": False, "status_code": None, "body": {"error": str(e)}} - - -def local_llm_request(prompt: str) -> str: - config = ChangAIConfig.get() - url = f"{config['URL'].rstrip('/')}/api/generate" - payload = {"model": config["LOCAL_LLM"], "prompt": prompt, "stream": False} - resp = _post_json(url, headers={}, payload=payload, timeout=120) - if not resp.get("ok"): - return f"Error: local LLM call failed ({resp.get('status_code')}): {resp.get('body')}" - text = (resp.get("body") or {}).get("response") - return (text or "").strip() or "Error: Empty response from local LLM." - - -def _get_gemini_vertex_config(config): - project_id = (config.get("gemini_project_id") or "").strip() - credentials_json = (config.get("gemini_json_content") or "").strip() - location = (config.get("gemini_location") or "").strip() - return project_id, credentials_json, location - - -def _throw_missing_vertex_field(project_id: str, location: str, credentials_json: str) -> None: - if not project_id: - frappe.throw( - _("Gemini Project ID is missing.

Please Go to Settings Page and enter your Gemini Project ID.
" - "Check Quick Start Guide 👇:
Click here
" - "ERPGulf.com.").format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), - title=_("Missing Gemini Project ID"), - ) - if not location: - frappe.throw( - _("Gemini Location is missing.

Please Go to Settings Page and enter your Gemini Location.
" - "Check Quick Start Guide 👇:
Click here
" - "ERPGulf.com.").format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), - title=_("Missing Gemini Location"), - ) - if not credentials_json: - frappe.throw( - _("Service Account Credentials are missing.

Please Go to Settings Page and enter your Service Account Credential.
" - "Check Quick Start Guide 👇:
Click here" - "ERPGulf.com." -).format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), - title=_("Missing Service Account Credentials"), - ) - - -def _build_vertex_gemini_client(project_id: str, location: str, credentials_json: str): - _throw_missing_vertex_field(project_id, location, credentials_json) - - service_account_info = json.loads(credentials_json) - creds = service_account.Credentials.from_service_account_info( - service_account_info, - scopes=["https://www.googleapis.com/auth/cloud-platform"], - ) - return genai.Client( - vertexai=True, - project=project_id, - location=location, - credentials=creds, - ) - - -def _get_api_key_client(config): - try: - api_key = config.get("gemini_api_key") - except Exception: - api_key = None - - if not api_key: - frappe.throw( - _( - "Gemini API key is not configured.

" - "You have two options to authenticate with Gemini:

" - "Option 1 (Free / API Key):
" - "Go to ChangAI Settings and enter your Gemini API Key.
" - "Get your free API key from " - "Google AI Studio.

" - "Option 2 (Vertex AI / Service Account):
" - "Fill in Gemini Project ID, Gemini Location, " - "and Service Account Credentials in Got to Settings Page.
" - "ChangAI Quick Start Guide 👇:
" - "Click here
" - "ERPGulf.com." - - ).format(CHANGAI_GUIDE_LINK,settingsUrl,ERPGULF_LINK), - title=_("Gemini Authentication Not Configured"), - ) - - return genai.Client(api_key=api_key) - - -def _build_gemini_client(config): - project_id, credentials_json, location = _get_gemini_vertex_config(config) - - if project_id or credentials_json or location: - return _build_vertex_gemini_client(project_id, location, credentials_json) - - return _get_api_key_client(config) - - -def _build_gemini_contents(prompt: str): - return [ - { - "role": "user", - "parts": [{"text": str(prompt)}], - } - ] - - -def _clean_gemini_response_text(text: str) -> str: - text = (text or "").strip() - if text.startswith("```"): - text = text.replace("```json", "").replace("```", "").strip() - return text - - -def _handle_gemini_api_exception(e: Exception) -> None: - if isinstance(e, google_exceptions.ResourceExhausted): - frappe.throw( - _("Gemini API quota exceeded.

Please wait and try again or upgrade your plan.
Check Quick Start Guide 👇:
" - "Click here
" - "ERPGulf.com." -).format(CHANGAI_GUIDE_LINK,ERPGULF_LINK), - - title=_("Gemini Quota Exceeded"), - ) - if isinstance(e, google_exceptions.Unauthenticated): - frappe.throw( - _("Gemini API key is invalid.

Please go to ChangAI Settings and enter a valid Gemini API Key.
" - "Check ChangAI Quick Start Guide 👇:
Click here
" - "ERPGulf.com." -).format(CHANGAI_GUIDE_LINK,ERPGULF_LINK), - title=_("Invalid Gemini API Key"), - ) - if isinstance(e, google_exceptions.PermissionDenied): - frappe.throw( - _("Gemini API permission denied.

Please check your API key permissions.
" - "Check ChangAI Quick Start Guide 👇:
Click here
" - "ERPGulf.com." -).format(CHANGAI_GUIDE_LINK,ERPGULF_LINK), - title=_("Gemini Permission Denied"), - ) - if isinstance(e, google_exceptions.InvalidArgument): - frappe.throw( - _("Invalid request to Gemini API: {0}
" - "Check ChangAI Quick Start Guide 👇:
" - "Click here
" - "ERPGulf.com.").format(str(e),CHANGAI_GUIDE_LINK,ERPGULF_LINK), - title=_("Gemini Invalid Request"), - ) - - frappe.log_error(frappe.get_traceback(), "Gemini API Unexpected Error") - frappe.throw( - _("Gemini API error: {0}
" - "Check ChangAI Quick Start Guide 👇:
" - "Click here
" - "ERPGulf.com.").format(str(e),CHANGAI_GUIDE_LINK,ERPGULF_LINK), - title=_("Gemini API Error"), - ) - - -def gemini_client(): - global _GEMINI_CLIENT,_GEMINI_CONFIG - if _GEMINI_CLIENT is None: - config = frappe.get_single(CHANGAI_SETTINGS) - _GEMINI_CONFIG = config - _GEMINI_CLIENT = _build_gemini_client(config) - return _GEMINI_CLIENT - -def call_gemini(prompt: str,sys_prompt: str) -> Union[str, Dict[str, Any]]: - try: - # frappe.clear_document_cache(CHANGAI_SETTINGS) - client = gemini_client() - - gemini_config = types.GenerateContentConfig( - system_instruction=sys_prompt, - ) - response = client.models.generate_content( - model=MODEL_ID, - config=gemini_config, - contents=_build_gemini_contents(prompt), - ) - return _clean_gemini_response_text(response.text) - - except frappe.exceptions.ValidationError: - raise - except Exception as e: - _handle_gemini_api_exception(e) - - -def _build_input_payload(task: str, prompt: str, question: Optional[str], - db_result_json: Optional[str], user_message: Optional[str]) -> Dict[str, Any]: - if task == "format_db": - return {"task": "format_db", "question": question or "", "db_result_json": db_result_json or "{}"} - if task == "helpdesk_task": - return {"task": "helpdesk_task", "user_message": user_message or prompt or ""} - return {"task": "llm", "user_input": prompt} - - -def _poll_until_done(get_url: str, headers: Dict) -> Any: - terminal = {"succeeded", "failed", "canceled"} - deadline = time.time() + 300 - last = None - while time.time() < deadline: - try: - poll = requests.get(get_url, headers=headers, timeout=120).json() - except Exception as e: - poll = {"raw_text": str(e)} - last = poll - status = poll.get("status") - if status in terminal: - if status == "succeeded": - return poll.get("output") - return {"Error": f"Model ended with status {status}", "details": poll} - time.sleep(2) - return {"Error": "Polling timed out", "details": last} - - -def remote_llm_request_deploy_test( - prompt: str = "", - task: str = "llm", - question: Optional[str] = None, - db_result_json: Optional[str] = None, - user_message: Optional[str] = None, -) -> Any: - config = ChangAIConfig.get() - headers = { - "Content-Type": APPLICATION_JSON, - "Prefer": "wait", - "Authorization": f"Bearer {config['API_TOKEN']}", - } - input_payload = _build_input_payload(task, prompt, question, db_result_json, user_message) - create = _post_json(config["deploy_url"], headers=headers, payload={"input": input_payload}, timeout=120) - - if not create.get("ok"): - return {"Error": "Create prediction failed", "status_code": create.get("status_code"), "details": create.get("body")} - - get_url = ((create.get("body") or {}).get("urls") or {}).get("get") - if not get_url: - return {"Error": "Missing get URL from deploy response", "details": create.get("body")} - - return _poll_until_done(get_url, headers) - - -def remote_embedder_request(formatted_q: str) -> Union[List[Any], str]: - config = ChangAIConfig.get() - payload = {"version": config["EMBED_VERSION_ID"], "input": {"user_input": formatted_q}} - headers = { - "Content-Type": APPLICATION_JSON, - "Prefer": "wait", - "Authorization": f"Bearer {config['API_TOKEN']}", - } - response = _post_json(config["URL"], headers, payload) - try: - if response: - return response["body"]["output"] - except Exception as e: - return "Error: " + str(e) - - def _safe_strip(v): if v is None: return "" @@ -1023,7 +185,6 @@ def _safe_strip(v): return json.dumps(v, ensure_ascii=False, default=str) return str(v).strip() - # Shared State class SQLState(TypedDict, total=False): filters:str @@ -1064,91 +225,23 @@ class SQLState(TypedDict, total=False): message:str stop_followup:bool + def route_action(state: SQLState) -> str: if state.get("stop_followup"): return "STOP_FOLLOW" if state.get("create_entity"): return "CREATE_ENTITY" - if state.get("open_report"): return "OPEN_REPORT" - return "CONTINUE" def fill_sql_prompt(question: str, context: str) -> str: return SQL_PROMPT.format(question=question, context=context) - -@lru_cache(maxsize=None) -def _word_is_erp(word: str) -> bool: - if len(word) <= 3: - return False - if word in _KEYWORDS_SET: - return True - for kw in _KEYWORDS_SET: - if word in kw or kw in word: - return True - if len(word) >= 4: - match = process.extractOne( - word, _KEYWORDS_LIST, scorer=fuzz.ratio, score_cutoff=70 - ) - if match: - return True - return False - - -STOP_WORDS = { - # English greetings / casual - "hi", "hello", "hey", "thanks", "thank", "please", "pls", - "ok", "okay", "yes", "no", "bye", "goodbye","have","has","had","do","does","did", - - # English question/helper words - "what", "which", "who", "whom", "whose", "where", "when", "why", "how", - "can", "could", "would", "should", "do", "does", "did", "is", "are", - "was", "were", "be", "been", "being", - - # English filler/common words - "the", "a", "an", "to", "for", "from", "of", "in", "on", "at", "by", - "with", "without", "and", "or", "but", "if", "then", "than", "as", - "this", "that", "these", "those", "it", "its", "there", "here", - - # English user command words - "show", "list", "give", "get", "find", "display", "tell", "me", - "need", "want", "make", "create", "check", "see", "view", - - # English time/common filters - "today", "yesterday", "tomorrow", "now", "current", "latest", - "last", "next", "this", "week", "month", "year", "daily", "weekly", - "monthly", "yearly", - - # Arabic greetings / casual - "مرحبا", "مرحبًا", "اهلا", "أهلا", "أهلًا", "السلام", "شكرا", "شكرًا", - "نعم", "لا", "طيب", "تمام", "مع السلامة", - - # Arabic question/helper words - "ما", "ماذا", "من", "متى", "أين", "اين", "كيف", "لماذا", "هل", "كم", - "أي", "اي", "الذي", "التي", "الذين", - - # Arabic filler/common words - "في", "من", "إلى", "الى", "على", "عن", "مع", "بدون", "و", "أو", "او", - "لكن", "إذا", "اذا", "ثم", "هذا", "هذه", "هؤلاء", "ذلك", "تلك", "هنا", - - # Arabic user command words - "اعرض", "عرض", "اظهر", "أظهر", "هات", "اعطني", "أعطني", "اريد", "أريد", - "احتاج", "ابحث", "تحقق", "شوف", - - # Arabic time/common filters - "اليوم", "أمس", "امس", "غدا", "غدًا", "الآن", "الان", "الحالي", - "الأخير", "الاخير", "هذا", "هذه", "الأسبوع", "الاسبوع", "الشهر", - "السنة", "العام", "يومي", "أسبوعي", "شهري", "سنوي", -} - - def tokenize_mixed(text): return re.findall(r'[\u0600-\u06FF]+|[a-zA-Z0-9]+', text.lower()) - def is_erp_query(master_match:bool, q: str, words_list: list,cut_off_perc:int) -> bool: if master_match: match = process.extract( @@ -1160,17 +253,13 @@ def is_erp_query(master_match:bool, q: str, words_list: list,cut_off_perc:int) - return { "matched_value": match, } - words = tokenize_mixed(q) - for word in words: - if words_list != THREAD_WORDS: if word in STOP_WORDS: continue if len(word) <= 2: continue - match = process.extractOne( word, words_list, @@ -1180,7 +269,6 @@ def is_erp_query(master_match:bool, q: str, words_list: list,cut_off_perc:int) - if match: return True - return False @@ -1200,7 +288,6 @@ def guardrail_router(state: SQLState) -> SQLState: query_type = "NON_ERP" frappe.log_error(frappe.get_traceback(), "Guardrail Router Error") return {**state, "query_type": query_type, "error": f"Error in guardrail router: {str(e)}"} - state["query_type"] = query_type publish_pipeline_update( request_id, @@ -1210,25 +297,6 @@ def guardrail_router(state: SQLState) -> SQLState: ) return state - -def send_non_erp_request(state: SQLState) -> SQLState: - qstn =state.get("question") - if not qstn: - return {**state, "non_erp_res": "", "error": "No question provided"} - # prompt = NON_ERP_PROMPT.format(question=qstn) - try: - # response = handle_non_erp_query(qstn) - response = non_erp_response(qstn) - # response = call_model(prompt, "llm") - if not response or not response.get("data"): - return {**state,"non_erp_res": "", "error": str(response)} - return {**state,"non_erp_res": response["data"], "error": None} - except frappe.exceptions.ValidationError: - raise - except Exception as e: - return {**state, "non_erp_res": "", "error": f"NON-ERP call failed: {e}"} - - def _parse_rewrite_response(raw: Any, user_qstn: str) -> Tuple[str, bool]: standalone = "" contains_values = False @@ -1242,7 +310,6 @@ def _parse_rewrite_response(raw: Any, user_qstn: str) -> Tuple[str, bool]: create_entity = False report_intent = None message=None - if isinstance(raw, dict): obj = raw elif isinstance(raw, str): @@ -1252,7 +319,6 @@ def _parse_rewrite_response(raw: Any, user_qstn: str) -> Tuple[str, bool]: standalone = raw.strip() else: standalone = str(raw).strip() - if isinstance(obj, dict): standalone = (obj.get("standalone_question") or "").strip() or standalone contains_values = bool(obj.get("contains_values")) @@ -1269,14 +335,10 @@ def _parse_rewrite_response(raw: Any, user_qstn: str) -> Tuple[str, bool]: stop_followup = bool(obj.get("stop_followup")) if stop_followup: message = obj.get("message") - elif isinstance(obj, list) and not standalone: standalone = json.dumps(obj) - return standalone or user_qstn.strip(), contains_values, entity_words,create_entity, doc,entity_name,report_name,open_report,report_intent,stop_followup,message -SQL_REWRITE_SYS_PROMPT = read_asset("sql_rewrite_sys_prompt.txt", base="prompts") -SQL_REWRITE_USER_PROMPT = read_asset("sql_rewrite_user_prompt.txt", base="prompts") def rewrite_question(state: SQLState) -> SQLState: report_intent = None request_id = state.get("request_id") @@ -1291,15 +353,12 @@ def rewrite_question(state: SQLState) -> SQLState: standalone, contains_values,entity_words,create_entity, doc,entity_name,report_name,open_report,report_intent,stop_followup,message = _parse_rewrite_response(raw, user_qstn) if report_intent: report_name_new = match_report_intent(report_intent) - - publish_pipeline_update( request_id, "question_rewrite_done", "Question rewritten", data={"formatted_q": standalone} ) - return { **state, "report_name":report_name_new or report_name or "", @@ -1316,10 +375,8 @@ def rewrite_question(state: SQLState) -> SQLState: "message":message if stop_followup else None, "stop_followup": stop_followup } - except frappe.exceptions.ValidationError: raise - except Exception as e: publish_pipeline_update( request_id, @@ -1331,157 +388,25 @@ def rewrite_question(state: SQLState) -> SQLState: return {**state, "error": str(e)} -def get_vs(istable: bool): - global _VS_TABLE, _VS_REPORT - - emb = get_embedding_engine() - if emb is None: - frappe.throw(_(EMBEDDING_ENGINE_NONE_MESSG)) - - app_path = frappe.get_app_path("changai") - - if istable: - if _VS_TABLE is None: - table_vs_path = os.path.join( - app_path, "changai", "api", "v2", - "fvs_stores", "erpnext", "table_fvs" - ) - - if not os.path.exists(table_vs_path): - frappe.throw(_("FAISS table store not found at {0}").format(table_vs_path)) - - _VS_TABLE = FAISS.load_local( - table_vs_path, - emb, - allow_dangerous_deserialization=True - ) - - return _VS_TABLE - - else: - if _VS_REPORT is None: - report_vs_path = os.path.join( - app_path, "changai", "api", "v2", - "fvs_stores", "erpnext", "report_fvs" - ) - - if not os.path.exists(report_vs_path): - frappe.throw(_("FAISS report store not found at {0}").format(report_vs_path)) - - _VS_REPORT = FAISS.load_local( - report_vs_path, - emb, - allow_dangerous_deserialization=True - ) - - return _VS_REPORT - - - -# def call_fvs_table_search(q: str) -> List[str]: -# hits = get_table_vs().similarity_search(q, k=20) -# out, seen = [], set() -# for h in hits: -# t = h.metadata.get("table") -# if t and t not in seen: -# seen.add(t) -# out.append(t) -# return out - -def check_memory_status() -> dict: - return { - "pid": os.getpid(), - "module": __name__, - "file": __file__, - "globals": { - "embedding_model": { - "loaded": _EMBEDDER_INSTANCE is not None, - "id": id(_EMBEDDER_INSTANCE), - }, - "table_vs": { - "loaded": _VS_TABLE is not None, - "id": id(_VS_TABLE), - }, - "full_fields_vs": { - "loaded": _FULL_FIELDS_VS is not None, - "id": id(_FULL_FIELDS_VS), - }, - "field_docs": { - "loaded": _FIELD_DOCS_CACHE is not None, - "id": id(_FIELD_DOCS_CACHE), - }, - "field_embs": { - "loaded": _FIELD_EMBS_CACHE is not None, - "id": id(_FIELD_EMBS_CACHE), - }, - "table_to_idx": { - "loaded": _TABLE_TO_IDX_CACHE is not None, - "id": id(_TABLE_TO_IDX_CACHE), - }, - "master_vs": { - "loaded": _VS_MASTER is not None, - "id": id(_VS_MASTER), - }, - "gemini_client": { - "loaded": _GEMINI_CLIENT is not None, - "id": id(_GEMINI_CLIENT), - }, - # "symspell": { - # "loaded": sym_spell is not None, - # "id": id(sym_spell), - # }, - "keywords": { - "loaded": _KEYWORDS_SET is not None, - "id": id(_KEYWORDS_SET), - }, - } +@frappe.whitelist(allow_guest=False) +def rewrite_question_dup(user_question: str, session_id: str = "test_session") -> dict: + state: SQLState = { + "question": user_question, + "session_id": session_id, + "request_id": f"test_{frappe.generate_hash(length=10)}", } + return rewrite_question(state) - -@lru_cache(maxsize=512) -def _get_cached_embedding(q: str, request_id: str) -> tuple: - # vec = get_local_embedding(q) - emb = get_embedding_engine() - - publish_pipeline_update( - request_id, - "embedding_end", - "get_embedding_engine ended" - ) - vec = emb.embed_query(q) - publish_pipeline_update( - request_id, - "embedding_query_done", - "embedding query done" - ) - return tuple(vec) # tuple for hashability - - +ENTITY_CREATION_PROMPT = read_asset("create_entity_prompt.txt", base="prompts") def create_entity(state:SQLState): request_id = state.get("request_id") res = call_fvs_table_search(True, state.get("formatted_q"), state.get("request_id")) - prompt = f"""You are selecting the correct ERPNext DocType for entity creation. -User query: -{state.get("formatted_q")} -Candidate DocTypes: -{res[:10]} -Rules: -- Choose only one DocType from the candidate list. -- Use only the given candidate DocTypes. -- Do not add the "tab" prefix. -- Return only valid JSON. -- Do not return explanations or extra text. -- If no suitable DocType is found, return {{"doctype": ""}}. -Output format: -{{"doctype": "chosen_doctype_name"}} - - """ + prompt =ENTITY_CREATION_PROMPT.format(question=state.get("formatted_q"),candidate_doctypes=res[:10]) publish_pipeline_update( request_id, "Detecting doctype for creation", "Detecting doctype for creation", - done=True -) + done=True) res = call_gemini(prompt,"") try: if isinstance(res, str): @@ -1492,55 +417,6 @@ def create_entity(state:SQLState): except json.JSONDecodeError as e: return {**state,"error":str(e)} - - -def call_fvs_table_search(get_table: bool, q: str, request_id: str) -> List[str]: - # get cached embedding - publish_pipeline_update( - request_id, - "Inside the Table Search Function", - _("Inside the Table Search Function") - ) - q_vec = np.array(_get_cached_embedding(q,request_id), dtype="float32") - publish_pipeline_update( - request_id, - "Completed Embed for Table Search Function", - _("Completed Embed for Table Search Function") - ) - - # use FAISS index directly instead of similarity_search - publish_pipeline_update( - request_id, - "q_vec_ready", - _("q_vec_ready") - ) - vs = get_vs(get_table) - publish_pipeline_update( - request_id, - "vs_ready", - _("vs_ready") - ) - scores, indices = vs.index.search(q_vec.reshape(1, -1), k=20) - publish_pipeline_update( - request_id, - "index_search_done", - _("index_search_done") - ) - - out, seen = [], set() - for idx in indices[0]: - if idx == -1: - continue - doc_id = vs.index_to_docstore_id[idx] - doc = vs.docstore.search(doc_id) - t = doc.metadata.get("table") if get_table else doc.metadata.get("report_name") - if t and t not in seen: - seen.add(t) - out.append(t) - return out - - - def _parse_json_list(raw: str) -> List[Any]: try: data = json.loads(raw) @@ -1549,166 +425,6 @@ def _parse_json_list(raw: str) -> List[Any]: return [] -from langchain_community.vectorstores import FAISS -import faiss - -def build_hnsw_index(embeddings): - dim = len(embeddings[0]) - - index = faiss.IndexHNSWFlat(dim, 32) # 32 = neighbors (tune this) - index.hnsw.efConstruction = 200 # build quality - index.hnsw.efSearch = 50 # search accuracy/speed tradeoff - - return index - -def call_retrieve_multi_line(user_question: str, request_id: str) -> Dict[str, Any]: - try: - top_tables = call_fvs_table_search(True, user_question, request_id) - publish_pipeline_update( - request_id, - "table_retrieval_done", - _("Tables retrieved") - ) - fields_candidates= call_fvs_field_search_global_k( - user_question, - selected_tables=top_tables, - k_total=40, - request_id=request_id - ) - publish_pipeline_update( - request_id, - "field_retrieval_done", - "Fields selected" - ) - return { - "selected_fields": fields_candidates, - "selected_tables": top_tables, - "top_tables": top_tables, - "top_fields": fields_candidates, - } - except frappe.exceptions.ValidationError: - raise - except Exception as e: - return {"selected_fields": {}, "selected_tables": [], "top_tables": [], "error": str(e)} - - -def call_fvs_field_search_global_k( - user_question: str, - selected_tables: List[str], - k_total: int = 40, - request_id: Optional[str] = None -) -> str: - if isinstance(selected_tables, str): - try: - selected_tables = json.loads(selected_tables) - except Exception: - selected_tables = [selected_tables] - if not user_question or not selected_tables: - return "" - - docs, embs, table_to_idx = load_field_matrix() - - q_vec = np.array( - _get_cached_embedding(user_question, request_id), - dtype="float32" - ) - - q_vec = q_vec / max(np.linalg.norm(q_vec), 1e-12) - - all_idxs = [] - - for t in selected_tables: - t = str(t).strip() - if not t: - continue - - candidates = [ - t, - f"tab{t}" if not t.startswith("tab") else t, - t.replace("tab", "", 1) if t.startswith("tab") else t, - ] - - for key in candidates: - if key in table_to_idx: - all_idxs.extend(table_to_idx[key]) - break - - if not all_idxs: - frappe.log_error( - title="ChangAI Field Search: No Indexes Found", - message=json.dumps({ - "user_question": user_question, - "selected_tables": selected_tables, - "sample_table_to_idx_keys": list(table_to_idx.keys())[:50], - }, indent=2, default=str) - ) - return "" - - sub_embs = embs[all_idxs] - scores = sub_embs @ q_vec - - top_global = np.argsort(-scores)[:k_total] - - grouped = {} - seen = set() - - for i in top_global: - doc_i = all_idxs[int(i)] - d = docs[doc_i] - - meta = getattr(d, "metadata", {}) or {} - - is_table = meta.get("is_table") - table = meta.get("table") - field = meta.get("field") or meta.get("name") - - if not table or not field: - continue - - key = (table, field) - if key in seen: - continue - - seen.add(key) - - name = field - - join_hint = meta.get("join_hint") - if isinstance(join_hint, dict): - linked_table = join_hint.get("table") - if linked_table: - name += f" -> {linked_table}" - elif isinstance(join_hint, str) and join_hint.strip(): - name += f" -> {join_hint.strip()}" - - opts = meta.get("options") - if opts: - if isinstance(opts, list): - name += " {" + ", ".join(str(o) for o in opts[:5]) + "}" - else: - name += " {" + str(opts) + "}" - - grouped.setdefault(table, { - "is_table": is_table, - "fields": [] - }) - - grouped[table]["fields"].append(name) - - if not grouped: - frappe.log_error( - title="ChangAI Field Search: Empty Grouped Result", - message=json.dumps({ - "user_question": user_question, - "selected_tables": selected_tables, - "all_idxs_count": len(all_idxs), - "top_global_count": len(top_global), - }, indent=2, default=str) - ) - return "" - return format_schema_context(grouped) - - # Node 1: Retrive with Fiass Vector Store. def schema_retriever(state: SQLState) -> SQLState: config = ChangAIConfig.get() @@ -1737,11 +453,9 @@ def hits_to_prompt_context(state:SQLState) -> SQLState: ctx=hits_to_schema_context(state["hits"],title="SCHEMA CONTEXT",max_fields_per_table=25) entity_context=state.get("entity_cards", []) full_context = ctx - if entity_context: full_context += "\n\nENTITY_CARDS:\n" full_context += "\n".join(entity_context) - return { **state, "context": full_context @@ -1808,151 +522,6 @@ def validate_sql(state: SQLState) -> SQLState: val = validate_sql_against_mapping(sql, mapping_data, dialect="mysql") return {**state, "validation": val} - -def remote_entity_embedder(q: str) -> Union[list, str]: - config = ChangAIConfig.get() - payload = {"version": config["entity_retriever"], "input": {"query": q}} - headers = { - "Content-Type": APPLICATION_JSON, - "Prefer": "wait", - "Authorization": f"Bearer {config['API_TOKEN']}", - } - response = _post_json(config["URL"], headers, payload) - return response - - -settingsUrl = frappe.utils.get_url( - "/app/changai-settings/ChangAI%20Settings" -) - - -def get_master_vs(): - global _VS_MASTER - try: - if _VS_MASTER is None: - emb = get_embedding_engine() - if emb is None: - frappe.throw(_(EMBEDDING_ENGINE_NONE_MESSG)) - - master_vs_path = frappe.get_site_path( - "private", "changai", "fvs_stores", "erpnext", "masterdata_fvs" - ) - if not os.path.exists(master_vs_path): - frappe.throw(_( - "FAISS MASTER store not found at {0}.

" - "Please open " - "Go to Settings Page" - "and click on the Update Master Data button in the Training tab.

" - "Check Quick Start Guide Here 👇
" - "Click here


" - "ERPGulf.com" - - ).format( - master_vs_path, - settingsUrl, - CHANGAI_GUIDE_LINK, - ERPGULF_LINK - )) - - _VS_MASTER = FAISS.load_local( - master_vs_path, - emb, - allow_dangerous_deserialization=True - ) - except Exception as e: - frappe.log_error(f"Error loading master vector store: {e}", "ChangAI Master VS Load Error") - - return _VS_MASTER - -import re - -def append_entity_field_to_schema(top_fields: str, table_name: str, field_name: str) -> str: - """ - Append field_name to the FIELDS section of table_name if missing. - Example: append customer_name into TABLE: tabCustomer block. - """ - - pattern = rf"(TABLE:\s*{re.escape(table_name)}\n.*?FIELDS:\n)(.*?)(?=\n\nTABLE:|\Z)" - - def replace_block(match): - header = match.group(1) - fields_block = match.group(2) - - # already exists - if re.search(rf"^- {re.escape(field_name)}(\s|$)", fields_block, re.MULTILINE): - return match.group(0) - - return header + fields_block.rstrip() + f"\n- {field_name}\n" - - return re.sub(pattern, replace_block, top_fields, count=1, flags=re.DOTALL) - - -def local_entity_embedder(q: str) -> List[Dict[str, Any]]: - hits = get_master_vs().similarity_search(q, k=20) - out, seen = [], set() - for h in hits: - entity_type = h.metadata.get("entity_type") # example: tabCustomer - entity_id = h.metadata.get("entity_id") # example: customer_name - entity_label = h.metadata.get("entity_label") - # if entity_type in state["selected_tables"]: - # state["selected_fields"] = append_entity_field_to_schema( - # top_fields=state["selected_fields"], - # table_name=entity_type, - # field_name=entity_id - # ) - - key = (entity_type, entity_label) - if key not in seen: - seen.add(key) - out.append({"entity_type": entity_type, "entity_id": entity_id, "entity_label": entity_label}) - return out - - -def call_entity_retriever(isreport: bool, qstn: str, state: Dict) -> Dict[str, Any]: - config = ChangAIConfig.get() - if config["REMOTE"] and config["llm"] == "QWEN3": - response = remote_entity_embedder(qstn) - - if not response.get("ok"): - frappe.log_error(f"Entity retriever failed: {response.get('body')}", "ChangAI Entity Retriever") - return {"raw": response, "cards": []} - - body = response.get("body") or {} - output = body.get("output") or {} - results = output.get("results") or [] - - cards = [r.get("entity_label") for r in results if r.get("entity_label")] - - return {"raw": body, "cards": cards} - else: - from changai.changai.api.v2.schema_utils import phonetic_match - entity_words = state.get("entity_words") - cards = [] - debug=[] - if entity_words is None: - return {"cards":[]} - for word in entity_words: - result = phonetic_match(isreport, word) - labels = result.get("entity_labels") or [] - debug.append({ - "word": word, - "result": result, - "labels": labels - }) - for label in labels: - if isreport: - try: - table_field, _ = labels.split(":", 1) - table, field = table_field.split(".", 1) - doctype = table.removeprefix("tab") - state["doc"] = doctype - except Exception: - pass - if label and label not in cards: - cards.append(label) - return {"cards": cards} - - # # Node 5:Repair Loop :Simple prompt for one more try. def repair_sqlquery(state: SQLState) -> SQLState: hints: List[str] = [] @@ -2010,217 +579,35 @@ def detect_specific_entities(state: SQLState) -> SQLState: "Go to Settings Page " "and click on the Update Master Data button in the Training tab.

" "Check Quick Start Guide Here 👇:
" - "Click here
" - "ERPGulf.com" - ).format(settingsUrl, CHANGAI_GUIDE_LINK, ERPGULF_LINK)) - - if res.get("is_stale") and res.get("days", 0) > 0: - frappe.throw(_( - "Your master data is {0} days old. " - "Because of this, results may not be accurate. " - "For better accuracy, please open " - "Go to Settings Page " - "and click on the Update Master Data button in the Training tab.

" - "Check Quick Start Guide Here 👇:
" - "Click here
" - "ERPGulf.com" - ).format(res.get("days"), settingsUrl, CHANGAI_GUIDE_LINK, ERPGULF_LINK)) - - out = call_entity_retriever(False, q, state) - return { - **state, - "entity_cards": out.get("cards") or [], - # "entity_raw": out.get("raw"), - } - except frappe.exceptions.ValidationError: - raise - except Exception as e: - frappe.log_error(f"Entity retriever failed: {e}", "ChangAI Entity Gate") - return {**state, "entity_cards": [], "entity_raw": {"error": str(e)}} - - -def route_after_entities(state: SQLState) -> str: - config = ChangAIConfig.get() - return "DIRECT" if config.get("retriever_structure") == "multi line" else "CONTEXT" - - -def route_guardrail(state: SQLState) -> str: - return "ERP" if state.get("query_type") == "ERP" else "NON_ERP" - -def clean_sql(s: Any) -> str: - if isinstance(s, dict): - s = s.get("output") or s.get("sql") or s.get("text") or json.dumps(s, ensure_ascii=False, default=str) - elif isinstance(s, list): - s = "\n".join(str(x) for x in s) - else: - s = "" if s is None else str(s) - - s = s.strip() - - if s.startswith("```"): - first_newline = s.find("\n") - if first_newline != -1: - header = s[:first_newline].strip().lower() - if header in {"```", "```sql"}: - s = s[first_newline + 1 :].lstrip() - - stripped = s.rstrip() - if stripped.endswith("```"): - stripped = stripped[:-3].rstrip() - s = stripped - - if s[:3].lower() == "sql" and (len(s) == 3 or s[3].isspace()): - s = s[3:].lstrip() - - return s.strip() - - -# # Router to decide next stage: -def router(state:SQLState) -> str: - if state.get("error"): - return "end" - val=state.get("validation",{}) - if val.get("ok"): - return "end" - tries=int(state.get("tries") or 0) - if tries < RETRY_LIMIT: - return "repair" - return "end" - - -def _parse_sql_ast(sql_text: str, dialect: str): - try: - return sqlglot.parse_one(sql_text, read=dialect), None - except Exception as e: - return None, str(e) - - -def _extract_tables(ast) -> Tuple[List[str], Dict[str, str]]: - base_tables = [] - alias_to_table = {} - for t in ast.find_all(exp.Table): - if not t.name: - continue - base_tables.append(t.name) - a = t.args.get("alias") - if a and a.name: - alias_to_table[a.name] = t.name - return list(dict.fromkeys(base_tables)), alias_to_table - - -def _extract_derived_aliases(ast) -> Set[str]: - derived = set() - for sq in ast.find_all(exp.Subquery): - a = sq.args.get("alias") - if a and a.name: - derived.add(a.name) - for cte in ast.find_all(exp.CTE): - a = cte.args.get("alias") - if a and a.name: - derived.add(a.name) - return derived - - -def _extract_select_aliases(ast) -> Set[str]: - aliases = set() - for sel in ast.find_all(exp.Select): - for proj in sel.expressions: - if isinstance(proj, exp.Alias) and proj.alias: - aliases.add(proj.alias) - return aliases - - -def _validate_qualified_col(col_name: str, qual: str, mapping: Dict, - alias_to_table: Dict, derived_aliases: Set) -> Optional[Tuple]: - if col_name == "*" or qual in derived_aliases: - return None - if qual in mapping: - if col_name not in mapping[qual]: - return (f"{qual}.{col_name}", qual) - return None - if qual in alias_to_table: - real = alias_to_table[qual] - if real in mapping and col_name not in mapping[real]: - return (f"{qual}.{col_name}", real) - return None - return (f"{qual}.{col_name}", None) - - -def _validate_unqualified_col(col_name: str, base_tables_set: Set, - mapping: Dict, select_aliases: Set, - unknown_cols: List, ambiguous: Set): - if col_name in select_aliases: - return - candidates = [t for t in base_tables_set if col_name in mapping.get(t, [])] - if len(candidates) == 0: - unknown_cols.append((col_name, None)) - elif len(candidates) > 1: - ambiguous.add(col_name) - - -def _validate_columns(ast, mapping: Dict, alias_to_table: Dict, - derived_aliases: Set, select_aliases: Set, - base_tables_set: Set) -> Tuple[List, Set]: - unknown_cols: List[Tuple[str, str]] = [] - ambiguous: Set[str] = set() - for col in ast.find_all(exp.Column): - if not col.name: - continue - if col.table: - result = _validate_qualified_col( - col.name, str(col.table), mapping, alias_to_table, derived_aliases - ) - if result: - unknown_cols.append(result) - else: - _validate_unqualified_col( - col.name, base_tables_set, mapping, select_aliases, unknown_cols, ambiguous - ) - return unknown_cols, ambiguous - - -def validate_sql_against_mapping( - sql_text: str, - mapping: Dict[str, List[str]], - dialect: str = "mysql" -) -> Dict[str, Any]: - result = { - "ok": True, - "unknown_tables": [], - "unknown_columns": [], - "ambiguous_columns": [], - "details": {"from_tables": [], "alias_to_table": {}, "derived_aliases": [], "select_aliases": []}, - } - - ast, parse_error = _parse_sql_ast(sql_text, dialect) - if parse_error: - result["ok"] = False - result["details"]["parse_error"] = parse_error - return result - - base_tables, alias_to_table = _extract_tables(ast) - derived_aliases = _extract_derived_aliases(ast) - select_aliases = _extract_select_aliases(ast) + "Click here
" + "ERPGulf.com" + ).format(settingsUrl, CHANGAI_GUIDE_LINK, ERPGULF_LINK)) - result["details"]["from_tables"] = base_tables - result["details"]["alias_to_table"] = alias_to_table - result["details"]["derived_aliases"] = sorted(derived_aliases) - result["details"]["select_aliases"] = sorted(select_aliases) + if res.get("is_stale") and res.get("days", 0) > 0: + frappe.throw(_( + "Your master data is {0} days old. " + "Because of this, results may not be accurate. " + "For better accuracy, please open " + "Go to Settings Page " + "and click on the Update Master Data button in the Training tab.

" + "Check Quick Start Guide Here 👇:
" + "Click here
" + "ERPGulf.com" + ).format(res.get("days"), settingsUrl, CHANGAI_GUIDE_LINK, ERPGULF_LINK)) - unknown_tables = [t for t in base_tables if t not in mapping] - if unknown_tables: - result["ok"] = False - result["unknown_tables"] = unknown_tables + out = call_entity_retriever(False, q, state) + return { + **state, + "entity_cards": out.get("cards") or [], + # "entity_raw": out.get("raw"), + } + except frappe.exceptions.ValidationError: + raise + except Exception as e: + frappe.log_error(f"Entity retriever failed: {e}", "ChangAI Entity Gate") + return {**state, "entity_cards": [], "entity_raw": {"error": str(e)}} - unknown_cols, ambiguous = _validate_columns( - ast, mapping, alias_to_table, derived_aliases, select_aliases, set(base_tables) - ) - if unknown_cols or ambiguous: - result["ok"] = False - result["unknown_columns"] = unknown_cols - result["ambiguous_columns"] = sorted(ambiguous) - return result def routeNonErpToAI(state: SQLState): question= state["question"] @@ -2238,142 +625,67 @@ def routeNonErpToAI(state: SQLState): raise except Exception as e: return {**state, "non_erp_res": "Model Calling Failed .Please try Again","error":str(e)} - - else: res= send_non_erp_request(state) return res +def send_non_erp_request(state: SQLState) -> SQLState: + qstn =state.get("question") + if not qstn: + return {**state, "non_erp_res": "", "error": "No question provided"} + try: + # response = handle_non_erp_query(qstn) + response = non_erp_response(qstn) + # response = call_model(prompt, "llm") + if not response or not response.get("data"): + return {**state,"non_erp_res": "", "error": str(response)} + return {**state,"non_erp_res": response["data"], "error": None} + except frappe.exceptions.ValidationError: + raise + except Exception as e: + return {**state, "non_erp_res": "", "error": f"NON-ERP call failed: {e}"} + +def route_after_entities(state: SQLState) -> str: + config = ChangAIConfig.get() + return "DIRECT" if config.get("retriever_structure") == "multi line" else "CONTEXT" + + +def route_guardrail(state: SQLState) -> str: + return "ERP" if state.get("query_type") == "ERP" else "NON_ERP" + + +# # Router to decide next stage: +def router(state:SQLState) -> str: + if state.get("error"): + return "end" + val=state.get("validation",{}) + if val.get("ok"): + return "end" + tries=int(state.get("tries") or 0) + if tries < RETRY_LIMIT: + return "repair" + return "end" def prepare_report_action(state: SQLState) -> SQLState: report_name = None q = state.get("formatted_q") or state.get("question") or "" request_id = state.get("request_id") report_name = state.get("report_name") - cards = call_entity_retriever(True, q, state) if state.get("contains_values") else {"cards": []} - # reports_listed = call_fvs_table_search(False, q, request_id) doctype = state.get("doc") if state.get("doc") else None result = [] - # for report in reports_listed[:30]: - # report_name = report.get("name") if isinstance(report, dict) else report - # result.append({ - # "report": report_name, - # "filters": get_report_filter_fields(report_name) - # }) result.append({"report":report_name,"filters": get_report_filter_fields(report_name)}) - prompt = f""" -Given the user query: -{q} -Detected DocType: use this value only for filtering by party type. -{doctype} -Available Report and Filter Fields: -{result} -after seeing the reports look wisely at the question and find the intent that speaks of report name take that intent and match with listed reports -and find the best matching report name. -Carefully identify the report intent from the user's question and match only that intent against the available report names. Do not use entity names, entity types, party names, customer names, supplier names, or filter values to choose the report, even if similar words appear in the listed report names. Entity information must be used only for filters, not for report_name selection. -even if the right report name is not ranked top find it from the top 10 and use it . -Entity Cards: -{cards} -Report selection MUST be based on the user's requested report type first. -Entity type (Customer, Supplier, Employee, etc.) is only used for filters and MUST NOT determine the report name. -Examples: -User: "show account statement for nova supplier" -Report: General Ledger -Filters: -{{ - "party_type": "Supplier", - "party": "nova supplier" -}} -TASK -Select exactly ONE report from the Available Reports and Filter Fields. -Step 1 - Understand User Intent -- Analyze the user's intent and meaning carefully. -- Match reports by meaning and business purpose, not only exact words. -- Consider synonyms and common ERP terminology. -Examples: -- "account statement", "customer statement", "supplier statement", "ledger statement" → usually General Ledger when available. -- "profit report" → Gross Profit. -- "trial balance" → Trial Balance. -- "balance sheet" → Balance Sheet. -Step 2 - Select the Best Report -- report_name MUST be selected only from the provided report list. -- Never invent a report name. -- Never return an empty report name. -- Choose the report whose purpose best matches the user's request. -- If multiple reports are similar, select the one whose available filters best satisfy the user's request. -- Report selection is more important than filter selection. -- Do not choose a report simply because a filter matches. -Step 3 - Apply Filters -- Use ONLY filters that exist in the selected report. -- Never invent filter names. -- Never invent filter values. -- Use Entity Card values whenever applicable. -- Apply only relevant filters supported by the selected report. -Party Filtering Rules -If: -- detected DocType is Customer, Supplier, Employee, Shareholder, Student, Member, or any party-type entity -AND -- selected report contains both party_type and party filters -THEN: -- party_type = detected DocType -- party = entity value from Entity Card -Example: -DocType = Customer -Entity Card: -customer_name: مؤسسة الإبداع التجارية -Filters: -{{ - "party_type": "Customer", - "party": "مؤسسة الإبداع التجارية" -}} -Important: -- Never infer party_type from the entity value. -- Always use the detected DocType. -- Prefer party_type + party over name-based filters whenever both are available. -Date Rules -Whenever a report requires date filtering: -- Convert relative dates into explicit dates. -- Use DD-MM-YYYY format only. -- Never use natural language dates. -Date Format Rules: -- Always output dates in YYYY-MM-DD format. -- Never use DD-MM-YYYY, MM-DD-YYYY, or natural language dates. -- Convert all relative dates to YYYY-MM-DD before returning filters. -Validation Before Answering -Verify: -1. report_name exists in Available Reports. -2. Every filter exists in the selected report. -3. Every filter value comes from the user query, Entity Cards, or a valid date conversion. -4. No invented report names. -5. No invented filters. -6. No empty report_name. -7. Report intent matches the user's request. -Return ONLY valid JSON: -{{ - "report_name": "", - "filters": {{ - "": "" - }} -}} -**Always use party_type when it is avaible for filtering if filtering by party name like customer, supplier,item etc -STRICT OUTPUT -- Return only JSON. -- No markdown. -- No explanations. -- No comments. -- Response must start with {{ and end with }}. -""" - + prompt = REPORT_ACTION_PROMPT.format( + question=q, + doctype=doctype or "", + available_reports=json.dumps(result, ensure_ascii=False, default=str), + entity_cards=json.dumps(cards, ensure_ascii=False, default=str)) response = call_model(prompt, "llm", "") - raw_response = str(response or "").strip() raw_response = raw_response.replace("```json", "").replace("```", "").strip() - frappe.logger().info(f"prepare_report_action raw response: {repr(raw_response)}") - if not raw_response: response = {"report_name": "", "filters": {}} else: @@ -2385,7 +697,6 @@ def prepare_report_action(state: SQLState) -> SQLState: "prepare_report_action JSON Error" ) response = {"report_name": "", "filters": {}} - if not isinstance(response, dict): response = {"report_name": "", "filters": {}} publish_pipeline_update( @@ -2404,19 +715,6 @@ def prepare_report_action(state: SQLState) -> SQLState: "entity_raw": cards, "error": None, } - - -# def route_action(state: SQLState): -# if state.get("create_entity") is True: -# qstn = state.get("user_question") -# response = call_fvs_table_search(False, qstn) -# return response -# return "CREATE_ENTITY" -# if state.get("open_report") is True: -# return "OPEN_REPORT" -# else: -# return "CONTINUE" - # Building the Workflow Graph workflow=StateGraph(SQLState) workflow.add_node("rewrite_question",rewrite_question) @@ -2431,12 +729,8 @@ def prepare_report_action(state: SQLState) -> SQLState: workflow.add_node("routeNonErpToAI",routeNonErpToAI) workflow.add_node("prepare_report_action", prepare_report_action) workflow.add_node("create_entity", create_entity) -# workflow.add_node("stop_follow_msg_api", stop_follow_msg_api) - - workflow.set_entry_point("guardrail_router") workflow.add_conditional_edges("guardrail_router",route_guardrail,{"ERP":"rewrite_question","NON_ERP":"routeNonErpToAI"}) -# workflow.add_edge("guardrail_router", "rewrite_question") workflow.add_edge("routeNonErpToAI", END) workflow.add_conditional_edges( "rewrite_question", @@ -2497,471 +791,6 @@ def execute_query(sql: str, doctypes: List[str]) -> Any: return {"error": f"SQL Execution Failed: {e}\n Check Quick Start Guide Here 👇:\n {CHANGAI_GUIDE_LINK}"} -@frappe.whitelist(allow_guest=False) -def support_bot(message: str) -> Dict[str, Any]: - user_email = frappe.session.user - full_name = frappe.get_value("User", frappe.session.user, "full_name") - prompt = SUPPORT_USER_PROMPT.format(user_message=message) - raw = call_gemini(prompt, SUPPORT_SYS_PROMPT) - output = json.loads(raw) - task_flag = (output.get("task_flag") or "UNKNOWN").strip() - ticket_id = output.get("ticket_id") - - if isinstance(ticket_id, str) and ticket_id.isdigit(): - ticket_id = int(ticket_id) - if not isinstance(ticket_id, int): - ticket_id = None - - if task_flag == "CREATE_TICKET": - try: - response = create_helpdesk_ticket(message, full_name, user_email) - return json.loads(response.get_data(as_text=True)) # ✅ unwrap Response → dict - except Exception as e: - return {"error": str(e)} - - if task_flag == "TICKET_DETAILS": - if not ticket_id: - return {"kind": "TICKET_DETAILS", "error": "Ticket id missing. Please say like: ticket 29"} - try: - response = get_user_tickets(ticket_id) - return json.loads(response.get_data(as_text=True)) # ✅ unwrap Response → dict - except Exception as e: - return {"error": str(e)} - - if task_flag == "GET_USER_TICKETS": - response = get_user_tickets() - return json.loads(response.get_data(as_text=True)) # ✅ unwrap Response → dict - - return {"kind": "UNKNOWN", "message": "Please describe the issue or provide a ticket number."} - -def save_logs( - user_question: Optional[str] = None, - formatted_q: Optional[str] = None, - context: Optional[str] = None, - sql: Optional[str] = None, - val: Any = None, - result: Any = None, - tries: Optional[int] = None, - err: Any = None, - formatted_result: Any = None, - tables:Any=None, - fields:Any=None, - entity_debug:Any=None, - type_=None -) -> str: - def to_json_if_needed(v: Any) -> Any: - if isinstance(v, (dict, list)): - return json.dumps(v, default=str, ensure_ascii=False) - return v - - MAX_LOG_LEN = 140 - doc = frappe.new_doc("ChangAI Logs") - doc.user_question = user_question - safe_question=(formatted_q[:137] + "..." if len(formatted_q) > MAX_LOG_LEN else formatted_q) - doc.rewritten_question = safe_question - doc.schema_retrieved = to_json_if_needed(context) - doc.sql_generated = to_json_if_needed(sql) - doc.validation = to_json_if_needed(val) - doc.tries = tries - doc.error = to_json_if_needed(err) - doc.result = to_json_if_needed(result) - doc.formatted_result = to_json_if_needed(formatted_result) - doc.tables = to_json_if_needed(tables) - doc.fields = to_json_if_needed(fields) - doc.entity = to_json_if_needed(entity_debug) - doc.type = type_ - doc.insert(ignore_permissions=True) - return doc.name - - -def format_data_conversationally(user_data: Any) -> str: - # Safe: CONVERSATION_TEMPLATE is a hardcoded internal template string. - # User SQL result is passed only as data context, not as template source. - # nosemgrep: frappe-semgrep-rules.rules.security.frappe-ssti - return render_template( - CONVERSATION_TEMPLATE, # nosemgrep: frappe-semgrep-rules.rules.security.frappe-ssti - - {"data": user_data} - ) - - -def format_data(qstn: str, sql_data: Any) -> Dict[str, str]: - if isinstance(sql_data, (dict, list)): - db_result_json = json.dumps(sql_data, ensure_ascii=False, default=str) - else: - db_result_json = str(sql_data) if sql_data is not None else "{}" - - sys_prompt = """ -You are ChangAI, a warm and intelligent business assistant. -Your job is to turn raw database results into clear, friendly, human-readable answers. -CONTENT RULES: -- Use BOTH the user question and the DB result JSON to form the answer. -- Use ONLY values present in the JSON. NEVER invent numbers or fields. -- If result is empty, respond warmly and suggest refining the search. -- Do NOT mention SQL, tables, fields, JSON, reasoning, or steps. - -TONE & STYLE: -- Warm, conversational, and helpful — like a knowledgeable friend, not a report. -- If the question is in Arabic, reply in natural Arabic — not translated English. -- Never respond with a cold, empty, or robotic answer. - -FORMATTING: -- Start with ONE relevant emoji matching the topic (📦💰🧾👥📊📅🔍💤📉) -- For 3+ items, use a bullet list: • Item — value -- If list exceeds shown items, state exactly how many remain. -- Keep answers brief (1–6 lines). Lead with the direct answer, then light context. - -CLOSING: -- End with ONE short, relevant follow-up question to keep the conversation going. -- Make it feel natural, not robotic. -Never list names or items in a comma-separated line. Ever. -OUTPUT: -- Markdown ALLOWED: **bold**, • bullets, emojis -- i dont want too much gap between the texts also gaps are not allowed between items listed. -- No JSON. No code blocks. No labels. No explanations. -- Output ONLY the final user-facing answer. Nothing else. -- if the user question is in english reply in english only very important. -if the user question is in arabic respond in arabic only. and if the question is in english respond answer also english -""" - user_prompt=f""" - QUESTION: - {qstn} - - DATABASE_RESULT_JSON: - {db_result_json} - """ - output = call_model(user_prompt,"llm",sys_prompt) - answer = str(output) - return {"answer": answer} - -def _collect_docs(hits: Union[List[Any], Dict, str]) -> List[Tuple[str, Dict]]: - def _to_txt_md(doc: Any) -> Tuple[str, Dict]: - if isinstance(doc, dict): - return doc.get("text", "") or "", doc.get("metadata", {}) or {} - if isinstance(doc, str): - return doc, {} - return "", {} - - if isinstance(hits, dict) and "message" in hits and isinstance(hits["message"], list): - hits = hits["message"] - - if isinstance(hits, (dict, str)) or hasattr(hits, "page_content"): - return [_to_txt_md(hits)] - return [_to_txt_md(d) for d in (hits or [])] - - -def _parse_tag(txt: str, tag: str) -> str: - m = re.search(rf"\[{re.escape(tag)}\]\s*(.+?)(?:\s*\||\s*$)", txt or "") - return m.group(1).strip() if m else "" - - -def _infer_type(txt: str) -> str: - if not (txt or "").startswith("["): - return "" - order = [ - ("TABLE", "table"), ("FIELD", "field"), ("JOIN", "join"), - ("METRIC", "metric"), ("ENUM", "enum"), ("PERIOD", "period"), - ("CURRENCY", "currency"), ("ENTITY", "entity") - ] - for tg, tp in order: - if txt.startswith(f"[{tg}]"): - return tp - return "" - - -class _SchemaAccumulator: - def __init__(self): - self.tables: List[str] = [] - self.fields_by_table: Dict[str, List[str]] = OrderedDict() - self.joins: List[str] = [] - self.metrics: List[Tuple[str, str, str]] = [] - self.periods: List[str] = [] - self.currencies: List[str] = [] - self.enums: OrderedDict = OrderedDict() - self.entities: List[Tuple[str, Dict]] = [] - - def add_table(self, t: str): - if t and t not in self.tables: - self.tables.append(t) - if t not in self.fields_by_table: - self.fields_by_table[t] = [] - - def add_field(self, tbl: str, fld: str): - if tbl and fld: - self.add_table(tbl) - fq = f"{tbl}.{fld}" - if fq not in self.fields_by_table[tbl]: - self.fields_by_table[tbl].append(fq) - - def add_join(self, on: str): - if on and on not in self.joins: - self.joins.append(on) - - def add_metric(self, mname: str, mexpr: str, mtbl: str): - if mtbl: - self.add_table(mtbl) - if mname: - tup = (mname, mexpr or "", mtbl or "") - if tup not in self.metrics: - self.metrics.append(tup) - - def add_period(self, pname: str): - if pname and pname not in self.periods: - self.periods.append(pname) - - def add_currency(self, code: str): - if code and code not in self.currencies: - self.currencies.append(code) - - def add_enum(self, tbl: str, fld: str, vals: Any): - if tbl: - self.add_table(tbl) - if tbl and fld: - key = f"{tbl}.{fld}" - if isinstance(vals, (list, tuple)): - vals = ", ".join([str(v) for v in vals]) - if key not in self.enums: - self.enums[key] = vals or "" - self.add_field(tbl, fld) - - def add_entity(self, ent_name: str, filt: Dict): - self.entities.append((ent_name, filt or {})) - - def sort(self): - self.tables.sort() - for t in self.fields_by_table: - if t not in self.tables: - self.tables.append(t) - for t in self.fields_by_table: - self.fields_by_table[t] = sorted( - self.fields_by_table[t], key=lambda s: s.split(".", 1)[1] - ) - self.joins.sort() - self.metrics.sort(key=lambda x: x[0]) - self.periods.sort() - self.currencies.sort() - self.enums = OrderedDict(sorted(self.enums.items(), key=lambda kv: kv[0])) - - -def _process_enum(txt: str, md: Dict, acc: _SchemaAccumulator): - tbl = md.get("table") or _parse_tag(txt, "TABLE") - fld = md.get("field") - if not fld: - ef = _parse_tag(txt, "ENUM") - if "." in ef: - tbl = tbl or ef.split(".", 1)[0].strip() - fld = ef.split(".", 1)[1].strip() - vals = md.get("values") - if vals is None: - vals = _parse_tag(txt, "VALUES") - acc.add_enum(tbl, fld, vals) - - -def _process_entity(txt: str, md: Dict, acc: _SchemaAccumulator): - ent_name = md.get("entity") or _parse_tag(txt, "ENTITY") or "Entity" - filt = md.get("filters") - if filt is None: - filt_txt = _parse_tag(txt, "FILTERS") - filt = {} - if filt_txt: - for part in [p.strip() for p in filt_txt.split(";") if p.strip()]: - if "=" in part: - k, v = part.split("=", 1) - filt[k.strip()] = [x.strip() for x in v.split(",") if x.strip()] - acc.add_entity(ent_name, filt) - - -def _get_table_name(txt: str, md: Dict) -> str: - return md.get("table") or _parse_tag(txt, "TABLE") - - -def _get_field_name(txt: str, md: Dict) -> str: - return md.get("field") or _parse_tag(txt, "FIELD").split(" (", 1)[0] - - -def _process_table_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - acc.add_table(_get_table_name(txt, md)) - - -def _process_field_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - acc.add_field(_get_table_name(txt, md), _get_field_name(txt, md)) - - -def _process_join_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - acc.add_join(md.get("on") or _parse_tag(txt, "ON")) - - -def _process_metric_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - acc.add_metric( - md.get("name") or _parse_tag(txt, "METRIC"), - md.get("expression") or _parse_tag(txt, "EXPR"), - _get_table_name(txt, md), - ) - - -def _process_period_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - acc.add_period(md.get("name") or _parse_tag(txt, "PERIOD")) - - -def _process_currency_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - acc.add_currency(md.get("code") or _parse_tag(txt, "CURRENCY")) - - -_DOC_PROCESSORS = { - "table": _process_table_doc, - "field": _process_field_doc, - "join": _process_join_doc, - "metric": _process_metric_doc, - "period": _process_period_doc, - "currency": _process_currency_doc, - "enum": _process_enum, - "entity": _process_entity, -} - - -def _process_doc(txt: str, md: Dict, acc: _SchemaAccumulator) -> None: - dtype = md.get("type") or _infer_type(txt) - processor = _DOC_PROCESSORS.get(dtype) - if processor: - processor(txt, md, acc) - - -def _append_table_lines( - lines: List[str], - acc: _SchemaAccumulator, - max_fields_per_table: int, -) -> None: - for tbl in acc.tables: - lines.append(f"Table: {tbl}") - lines.append("Fields:") - fields = acc.fields_by_table.get(tbl, []) - - if not fields: - lines.append(" -") - lines.append("") - continue - - lines.extend(f" - {field}" for field in fields[:max_fields_per_table]) - - extra_count = len(fields) - max_fields_per_table - if extra_count > 0: - lines.append(f" # +{extra_count} more") - - lines.append("") - - -def _append_simple_section(lines: List[str], title: str, items: List[str]) -> None: - if not items: - return - - lines.append(f"{title}:") - lines.extend(f" - {item}" for item in items) - lines.append("") - - -def _append_metric_lines(lines: List[str], acc: _SchemaAccumulator) -> None: - if not acc.metrics: - return - - lines.append("Metrics:") - for metric_name, metric_expr, metric_table in acc.metrics: - suffix = f" # table: {metric_table}" if metric_table else "" - line = f" - {metric_name}: {metric_expr}{suffix}" if metric_expr else f" - {metric_name}{suffix}" - lines.append(line) - lines.append("") - - -def _append_enum_lines(lines: List[str], acc: _SchemaAccumulator) -> None: - if not acc.enums: - return - - lines.append("Enums:") - for key, values in acc.enums.items(): - lines.append(f" - {key}: {values}" if values else f" - {key}") - lines.append("") - - -def _format_filter_value(value: Any) -> str: - if isinstance(value, (list, tuple)): - return ", ".join(str(item) for item in value) - return str(value) - - -def _append_entity_lines( - lines: List[str], - acc: _SchemaAccumulator, - show_entity_filters_yaml: bool, -) -> None: - if not acc.entities: - return - - lines.append("Entities:") - for entity, filters in acc.entities: - if show_entity_filters_yaml and isinstance(filters, dict) and filters: - lines.append(f" - Entity: {entity}") - lines.append(" Filters:") - for key, value in filters.items(): - lines.append(f" {key}: {_format_filter_value(value)}") - continue - - lines.append(f" - Entity: {entity}, Filters: {filters if filters else '{}'}") - - -def _trim_trailing_blank_lines(lines: List[str]) -> None: - while lines and not lines[-1].strip(): - lines.pop() - - -def _build_context_lines( - acc: _SchemaAccumulator, - title: str, - max_fields_per_table: int, - show_entity_filters_yaml: bool, -) -> List[str]: - lines: List[str] = [title] - - _append_table_lines(lines, acc, max_fields_per_table) - - if acc.joins: - lines.append("Join:") - lines.extend(f" {join}" for join in acc.joins) - lines.append("") - - _append_metric_lines(lines, acc) - _append_simple_section(lines, "Periods", acc.periods) - _append_simple_section(lines, "Currencies", acc.currencies) - _append_enum_lines(lines, acc) - _append_entity_lines(lines, acc, show_entity_filters_yaml) - - _trim_trailing_blank_lines(lines) - return lines - - -def hits_to_schema_context( - hits: Union[List[Any], Dict, str], - title: str = "SCHEMA CONTEXT", - max_fields_per_table: int = 20, - sort_sections: bool = True, - show_entity_filters_yaml: bool = True -) -> str: - acc = _SchemaAccumulator() - for txt, md in _collect_docs(hits): - _process_doc(txt, md, acc) - if sort_sections: - acc.sort() - lines = _build_context_lines(acc, title, max_fields_per_table, show_entity_filters_yaml) - return "\n".join(lines) - - -def debug_entity_retriever(q: str,state: SQLState): - resp = remote_entity_embedder(q) # this returns {"ok":..., "body":...} - return { - "query": q, - "raw_response": resp, - "parsed_entity_cards": call_entity_retriever(False, q,state) - } - - def _invoke_pipeline(user_question: str, chat_id: str, request_id: str,sendNonErptoAI: bool = False): initial_state: SQLState = { "question": user_question or "", @@ -2987,18 +816,16 @@ def _invoke_pipeline(user_question: str, chat_id: str, request_id: str,sendNonEr def _handle_non_erp_(non_erp_res,formatted_q,err,user_question: str,request_id:str, chat_id: str) -> Dict: - if not non_erp_res: if err: frappe.log_error(err, "ChangAI NON_ERP Error") save_logs(user_question=user_question, formatted_q="Not formatted as its NONERP",err=err, result=non_erp_res,type_="NonERP") - return { "Question": user_question, "Formatted-Question": formatted_q, "Bot": err if err else "⚠️ Could not get a response. Please try again.", } - + if not err and non_erp_res: try: publish_pipeline_update(request_id, "mapped to non-erp", "Mapped as NON-ERP") @@ -3007,35 +834,9 @@ def _handle_non_erp_(non_erp_res,formatted_q,err,user_question: str,request_id:s except Exception as e: frappe.log_error(f"Failed to save NON_ERP logs: {e}", "ChangAI Logs") save_logs(user_question=user_question, formatted_q="Not formatted as its NONERP",err=str(e), result=non_erp_res,type_="NonERP") - - return {"Question": user_question, "Formatted-Question": formatted_q, "Bot": non_erp_res} -def _get_sql_error_message(err: Any, val: Dict) -> str: - # if err: - # frappe.log_error(err, "ChangAI SQL Pipeline Error") - # return "⚠️ The model encountered an error generating your query. Please try the same Question again." - - error_text = (val.get("error") or "").strip() - - if not error_text: - return "⚠️ Could nprocess your request. Please try rephrasing." - - if "Empty SQL from LLM" in error_text: - return "⚠️ The model could not generate a SQL query for your question. Please try rephrasing." - - if "does not exist in schema" in error_text: - return f"⚠️ The model generated an invalid table reference. {error_text}" - - if "could not be resolved" in error_text: - return f"⚠️ The model generated an invalid field reference. {error_text}" - - if "parse" in error_text.lower() or "syntax" in error_text.lower() or "expected" in error_text.lower(): - return "⚠️ The model generated invalid SQL syntax. Please try rephrasing." - - return f"⚠️ The model generated an invalid query. {error_text}" - def _handle_sql_result( memory_status: Dict, request_id: str, @@ -3057,7 +858,6 @@ def _handle_sql_result( selected_tables = selected_tables or [] fields = fields or "" formatted_q = formatted_q or user_question - try: request_id = request_id or final.get("request_id") org_sql = final.get("sql") or sql @@ -3067,28 +867,21 @@ def _handle_sql_result( except Exception as e: # err = str(e) final["error"] = str(e) - publish_pipeline_update(request_id, "sql_executed", "Query executed") - entity_words = final.get("entity_words") or [] - except Exception as e: final["error"] = str(e) return {"ok": False, "error": f"SQL Execution Failed: {e}"} - context = (final.get("context") or final.get("selected_fields") or fields or "")[:800] contains_values = final.get("contains_values") or entity_debug.get("contains_values") or "" err = final.get("error") - formatted_result = format_data(user_question, sql_result) - publish_pipeline_update( request_id, "format_data_completed", "Completed Formatting Result", done=True ) - if not err: try: save_turn_2( @@ -3144,29 +937,11 @@ def _handle_sql_result( "EntityDebug": entity_debug if entity_debug.get("contains_values") else None, "Bot": formatted_result, } - +RETRY_PROMPT = read_asset("retry_sys_prompt.txt",base="prompts") +RETRY_USER_PROMPT = read_asset("retry_user_prompt.txt",base="prompts") def retry_sql(sql, error, formatted_q, sql_prompt): - retry_prompt = SQL_SYS_PROMPT + """ - -═══ RETRY MODE — STRICT FIX REQUIRED ═══ -STEP 1: Read the failed SQL and error message. -STEP 2: Find the broken field/table. -STEP 3: Check SCHEMA CONTEXT — does it exist? - YES → fix the syntax. - NO → remove it, find correct field from SCHEMA CONTEXT. -STEP 4: Verify every remaining field exists in SCHEMA CONTEXT. -STEP 5: Output fixed SQL. NEVER output the same broken SQL again.""" - - user_prompt = sql_prompt + f""" - -Failed SQL: {sql} -Error: {error} -User Question: {formatted_q} - -DO NOT repeat the same SQL. -DO NOT use the field mentioned in the error. -Find the correct field from SCHEMA CONTEXT and fix it.""" - + retry_prompt = SQL_SYS_PROMPT + RETRY_PROMPT + user_prompt = sql_prompt + RETRY_USER_PROMPT.format(sql=sql,error=error,formatted_q=formatted_q) try: rewritten = call_gemini(user_prompt, sys_prompt=retry_prompt) rewritten_json = json.loads(rewritten) @@ -3174,272 +949,18 @@ def retry_sql(sql, error, formatted_q, sql_prompt): retried_orm = clean_sql(rewritten_json.get("orm") or "") except Exception: return "", "", {"ok": False, "error": "Retry failed to parse response"} - if not retried_sql: return "", "", {"ok": False, "error": "Retry returned empty SQL"} - val_res = validate_sql_schema(retried_sql) return retried_sql, retried_orm, val_res - -def get_last_thread_message(chat_id: str): - data = frappe.get_all( - "ChangAI Chat History", - filters={"session_id": chat_id}, - fields=["content"], - order_by="creation asc" - ) - - for row in reversed(data): - try: - msg = json.loads(row["content"]) - # human_msg = msg[-2]["human"] - msg_type = msg[-2]["type"] - return msg_type - - except Exception: - pass - - return "" - - -THREAD_WORDS = [ - # English confirmation - "yes", "yep", "yeah", "yup", "yes please","list", - "of course", "sure", "surely", "absolutely", - "definitely", "certainly", "indeed", "correct", "ofcourse", - "right", "exactly", "precisely", - "ok", "okay", "fine", "alright", "go ahead", - "do it", "show me", "please", "go on", - "continue", "proceed", "why not", - "aye", "affirmative", "true", "agreed", - "hmm", "hm", "umm", "uh", "ah","give", - "interesting", "i see", "got it", "ok got it", - "and", "so", "then", "also", "but", - "what", "how", "when", "who", "where", "why", - "more", "less", "again", "another", "other", - "next", "previous", "back", "forward", - "noted", "understood", "makes sense", - "okay okay", "fine fine", "sure sure","show", - # Arabic confirmation - "نعم", "أجل", "بالتأكيد", "طبعاً", "حسناً", - "موافق", "صحيح", "بالضبط", "تماماً", "إي", - "ماشي", "تمام", "أوكي", "يلا", "استمر", - "كمّل", "واضح", "فاهم", "مفهوم", "اوك", - # Arabic neutral / continuation - "و", "ثم", "لكن", "أيضاً", "كذلك", - "ماذا", "كيف", "متى", "من", "أين", "لماذا", - "أكثر", "أقل", "مرة أخرى", "التالي", "السابق", - "حسناً حسناً", "تمام تمام", "مزيد", "غيره", - # Arabic rejection - "لا", "لأ", "لا شكراً", "إلغاء", "توقف", - "اتركه", "مش محتاج", "مو صح", "خطأ", -] - def is_thread_erp(q:str,chat_id:str): msg_type = get_last_thread_message(chat_id) - if msg_type == "erp" and is_erp_query(False,q, THREAD_WORDS,85): + if msg_type == "erp" and is_erp_query(False,q, THREAD_WORDS,90): return True else: return False -from rapidfuzz import fuzz -@frappe.whitelist() -def find_similar_log_question(new_question:str, threshold: int = 90): - logs = frappe.get_all( - "ChangAI Logs", - fields=["name", "user_question", "sql_generated","rewritten_question","fields","tables","error","entity","result"], - limit_page_length=500 - ) - - best_match = None - best_score = 0 - for log in logs: - score = fuzz.token_set_ratio(new_question, log.rewritten_question) - - if score > best_score: - best_score = score - best_match = log - - if best_score >= threshold: - return { - "matched": True, - "score": best_score, - "log_name": best_match.name, - "question": best_match.user_question, - "sql": best_match.sql_generated, - "rewritten_question":best_match.rewritten_question, - "fields":best_match.fields, - "tables":best_match.tables, - "entity_debug":best_match.entity, - "result":best_match.result, - "error":best_match.error - } - - return { - "matched": False, - "score": best_score - } - -def _error_response(memory_status, user_question, formatted_q, context, - selected_tables, fields, sql, validation, - entity_debug, tries, error, err): - return { - "Memory Status": memory_status, - "Question": user_question, - "Formatted_Question": formatted_q, - "Context": (context or "")[:800], - "Tables": selected_tables, - "Fields": fields, - "SQL": sql, - "Validation": validation, - "EntityDebug": entity_debug, - "Tries": tries, - "Error": error, - "Result": [], - "Bot": _get_sql_error_message(error, validation), - } - - -_WARMUP_COUNT=0 -def load_on_startup(): - global _WARMUP_COUNT,_EMBEDDER_INSTANCE, _VS_TABLE, _FULL_FIELDS_VS, _VS_MASTER, _FIELD_DOCS_CACHE, sym_spell, _GEMINI_CLIENT - _WARMUP_COUNT+=1 - frappe.log_error( - title=f"ChangAI Warmup called | PID {os.getpid()} | Count {_WARMUP_COUNT}", - message="load_on_startup triggered" - ) - - # If all are already loaded, skip - if all([ - _EMBEDDER_INSTANCE is not None, - _VS_TABLE is not None, - _FULL_FIELDS_VS is not None, - _VS_MASTER is not None, - _FIELD_DOCS_CACHE is not None, - sym_spell is not None, - _GEMINI_CLIENT is not None, - ]): - frappe.log_error( - title=f"ChangAI Warmup skipped | PID {os.getpid()}", - message="Already loaded in this worker" - ) - return - message=f"PID={os.getpid()} | module={__name__} | file={__file__} | loaded={_EMBEDDER_INSTANCE is not None} | id={id(_EMBEDDER_INSTANCE)}" - - try: - load_non_erp_data() - get_embedding_engine() - get_vs() - load_field_matrix() - gemini_client() - get_master_vs() - _init_keywords() - config = ChangAIConfig.get() - get_polly_client(config) - frappe.log_error( - title="ChangAI Warmup Completed", - message=frappe.get_traceback() # full stack trace - ) - except Exception as e: - frappe.log_error( - title="ChangAI Warmup Failed", - message=frappe.get_traceback() # full stack trace - ) - return message - - -def _init_keywords(): - global _KEYWORDS_SET, _KEYWORDS_LIST - if not _KEYWORDS_SET: - _KEYWORDS_SET = set(kw.lower() for kw in BUSINESS_KEYWORDS) - _KEYWORDS_LIST = list(_KEYWORDS_SET) - - # ✅ pre-warm cache — run every keyword through _word_is_erp at startup - for kw in _KEYWORDS_LIST: - _word_is_erp(kw) # result gets cached — first real request is instant - - - -def get_embedding_engine_test(): - global _EMBEDDER_INSTANCE - import time, os - - before_id = id(_EMBEDDER_INSTANCE) - before_loaded = _EMBEDDER_INSTANCE is not None - - if _EMBEDDER_INSTANCE is not None: - return { - "before_loaded": before_loaded, - "before_id": before_id, - "pid": os.getpid(), - "result": "returned_cached" - } - - t3 = time.time() - model_path = _get_model_path() - - _EMBEDDER_INSTANCE = HuggingFaceEmbeddings( - model_name=model_path, - model_kwargs={"device": "cpu", "trust_remote_code": True}, - encode_kwargs={"normalize_embeddings": True}, - ) - - return { - "before_loaded": before_loaded, - "before_id": before_id, - "after_loaded": _EMBEDDER_INSTANCE is not None, - "after_id": id(_EMBEDDER_INSTANCE), - "pid": os.getpid(), - "load_time": time.time() - t3, - "result": "loaded_now" - } - - -_NON_ERP_DATA = None -_NON_ERP_QUESTIONS = None -_NON_ERP_RESPONSE_MAP = None - - -def load_non_erp_data(): - global _NON_ERP_DATA, _NON_ERP_QUESTIONS, _NON_ERP_RESPONSE_MAP - - if _NON_ERP_DATA is not None: - return _NON_ERP_QUESTIONS, _NON_ERP_RESPONSE_MAP - try: - _NON_ERP_DATA = read_asset("non_erp_combined.processed.json") - except Exception as e: - frappe.log_error(f"Failed to load NON-ERP data: {e}", "ChangAI NON-ERP Data Load Error") - _NON_ERP_DATA = [] - - _NON_ERP_QUESTIONS = [] - _NON_ERP_RESPONSE_MAP = {} - - for item in _NON_ERP_DATA: - q = item.get("user_input") - if not q: - continue - - _NON_ERP_QUESTIONS.append(q) - _NON_ERP_RESPONSE_MAP[q] = item.get("response") - - return _NON_ERP_QUESTIONS, _NON_ERP_RESPONSE_MAP - - -def non_erp_response(non_erp_q: str) -> Optional[str]: - questions, response_map = load_non_erp_data() - result = process.extractOne( - non_erp_q, - questions, - scorer=fuzz.WRatio, - score_cutoff=65 - ) - if not result: - return {"data":"Hey Iam ChangAI from ERPGulf,iam here to help you with your queries..."} - matched_q = result[0] - return {"data": response_map.get(matched_q, "Hey Iam ChangAI from ERPGulf,iam here to help you with your queries...")} - - @frappe.whitelist(allow_guest=False) def run_text2sql_pipeline(user_question: str, chat_id: str, request_id: str, sendNonErptoAI: bool = False) -> Dict: err = "" @@ -3499,7 +1020,7 @@ def run_text2sql_pipeline(user_question: str, chat_id: str, request_id: str, sen return { "open_report": True, "report_name": final.get("report_name"), - "filters": final["filters"], + "filters": final.get("filters") or {}, "reports_filter_before_call": final.get("reports_filter_before_call"), "entity_raw": final.get("entity_raw"), "question_rewritten": formatted_q @@ -3518,15 +1039,14 @@ def run_text2sql_pipeline(user_question: str, chat_id: str, request_id: str, sen tables=None, fields=None, entity_debug=None, - type="NonERP" + type_="NonERP" ) publish_pipeline_update( - request_id, - "Stop follow-up detected", - "Stop follow-up detected", - data={"message": final.get("message")}, - done=True -) + request_id, + "Stop follow-up detected", + "Stop follow-up detected", + data={"message": final.get("message")}, + done=True) return { "Model returned SQL": None, "context": None, @@ -3541,7 +1061,7 @@ def run_text2sql_pipeline(user_question: str, chat_id: str, request_id: str, sen "Entity Values present ?": None, "Validation": None, "Error": err, - "result": fNone, + "result": None, "EntityDebug": None, "Bot": final.get("message"), } @@ -3555,27 +1075,13 @@ def run_text2sql_pipeline(user_question: str, chat_id: str, request_id: str, sen except Exception as e: frappe.log_error(e, "Error occurred while fetching final values") err = final.get("error") - - # # # guard empty sql - # # # if not sql: - # # # return _error_response(memory_status, user_question, formatted_q, context, - # # # selected_tables, fields, sql, - # # # {"ok": False, "error": "SQL is empty"}, - # # # entity_debug, 0, "SQL not valid or missing", err) - # retried_sql1, retried_orm1, retry1_val_res = retry_sql(retried_sql, retry_val_res.get("error"), formatted_q, sql_prompt) - # if retry1_val_res.get("ok"): - # return _handle_sql_result(memory_status,request_id, sql_prompt, final, retried_sql1, retried_orm1, - # formatted_q, fields, selected_tables, retry1_val_res, - # entity_debug, user_question, chat_id) res = validate_sql_schema(sql) publish_pipeline_update(request_id, "sql_validated", _("SQL validation Completed")) - # valid on first try if res.get("ok") and sql.upper().startswith("SELECT"): return _handle_sql_result(memory_status,request_id, sql_prompt, final, sql, orm, formatted_q, fields, selected_tables, res, entity_debug, user_question, chat_id) - # retry 2 retried_sql2, retried_orm2, retry2_val_res = retry_sql(sql, res.get("error"), formatted_q, sql_prompt) if retry2_val_res.get("ok"): @@ -3595,34 +1101,3 @@ def run_text2sql_pipeline(user_question: str, chat_id: str, request_id: str, sen return _error_response(memory_status, user_question, formatted_q, context, selected_tables, fields, retried_sql2 or sql, retry2_val_res, entity_debug, 2, final_error, err) - -from frappe.desk.query_report import get_script -def get_report_filter_fields(report_name: str): - try: - script = get_script(report_name).get("script") or "" - except Exception: - return [] - - fieldnames = re.findall( - r'fieldname\s*:\s*["\']([^"\']+)["\']', - script - ) - - return "|".join(dict.fromkeys(fieldnames)) - - -def match_report_intent(report_intent: str): - choices = list(REPORT_INTENT_MAP.keys()) - match = process.extractOne( - report_intent.lower(), - choices, - scorer=fuzz.WRatio, - score_cutoff=75 - ) - if not match: - return "" - - matched_intent = match[0] - return REPORT_INTENT_MAP[matched_intent] - - diff --git a/changai/changai/api/v2/tts.py b/changai/changai/api/v2/tts.py new file mode 100644 index 0000000..db3e20b --- /dev/null +++ b/changai/changai/api/v2/tts.py @@ -0,0 +1,93 @@ +import re +import base64 +import boto3 +import frappe +from typing import Any, Dict, Optional +from changai.changai.api.v2.schema_utils import ChangAIConfig + +_POLLY_CLIENT = None + + +def get_polly_client(config): + global _POLLY_CLIENT + + if _POLLY_CLIENT is None: + _POLLY_CLIENT = boto3.client( + "polly", + aws_access_key_id=(config.get("aws_access_key_id") or "").strip(), + aws_secret_access_key=(config.get("aws_secret_access_key") or "").strip(), + region_name=(config.get("aws_region") or "us-east-1"), + ) + return _POLLY_CLIENT + +def build_ssml(text: str) -> str: + parts = [] + current = [] + current_lang = None + + for token in text.split(): + lang = "ar-AE" if re.search(r'[\u0600-\u06FF]', token) else "en-US" + + if current_lang is None: + current_lang = lang + + if lang != current_lang: + parts.append( + f'{" ".join(current)}' + ) + current = [token] + current_lang = lang + else: + current.append(token) + + if current: + parts.append( + f'{" ".join(current)}' + ) + + return "" + " ".join(parts) + "" +@frappe.whitelist(allow_guest=False) +def synthesize_tts(text: str, voice_id: Optional[str] = None) -> Dict[str, Any]: + config = ChangAIConfig.get() + if not bool(config.get("enable_voice_chat")): + return {"ok": False, "error": "Voice chat is disabled in settings.", "provider": "browser"} + aws_access_key_id = (config.get("aws_access_key_id") or "").strip() + aws_secret_access_key = (config.get("aws_secret_access_key") or "").strip() + if not aws_access_key_id or not aws_secret_access_key: + return {"ok": False, "error": "AWS Polly credentials are missing.", "provider": "browser"} + cleaned_text = re.sub(r"<[^>]*>", " ", text or "") + cleaned_text = re.sub(r"\s+", " ", cleaned_text).strip() + if not cleaned_text: + return {"ok": False, "error": "Text is empty.", "provider": "browser"} + + if len(cleaned_text) > 2500: + cleaned_text = cleaned_text[:2500] + + try: + polly_client = get_polly_client(config) + voice = (voice_id or config.get("polly_voice_id") or "Zayd").strip() or "Zayd" + ssml_text = build_ssml(cleaned_text) + response = polly_client.synthesize_speech( + Text=ssml_text, + OutputFormat="mp3", + VoiceId="Zayd", + Engine="neural", + TextType="ssml", +) + stream = response.get("AudioStream") + if stream is None: + return {"ok": False, "error": "Polly did not return audio stream.", "provider": "browser"} + + audio_bytes = stream.read() + audio_base64 = base64.b64encode(audio_bytes).decode("utf-8") + return { + "ok": True, + "provider": "polly", + "mime_type": "audio/mpeg", + "audio_base64": audio_base64, + "voice_id": voice, + } + except Exception as e: + frappe.log_error(frappe.get_traceback(), "ChangAI Polly TTS Error") + return {"ok": False, "error": str(e), "provider": "browser"} + diff --git a/changai/changai/doctype/changai_settings/changai_settings.js b/changai/changai/doctype/changai_settings/changai_settings.js index 7088c74..82c086b 100644 --- a/changai/changai/doctype/changai_settings/changai_settings.js +++ b/changai/changai/doctype/changai_settings/changai_settings.js @@ -170,7 +170,7 @@ frappe.ui.form.on("ChangAI Settings", { applyTooltips(frm, fieldsWithTooltips); frm.add_custom_button(__('Download Embedding Model'), () => { frappe.call({ - method: "changai.changai.api.v2.text2sql_pipeline_v2.download_model", + method: "changai.changai.api.v2.retrieve.download_model", freeze: true, freeze_message: "Downloading Model...", callback(r) { diff --git a/changai/changai/prompts/create_entity_prompt.txt b/changai/changai/prompts/create_entity_prompt.txt new file mode 100644 index 0000000..bf155ab --- /dev/null +++ b/changai/changai/prompts/create_entity_prompt.txt @@ -0,0 +1,18 @@ +You are selecting the correct ERPNext DocType for entity creation. + +User query: +{question} + +Candidate DocTypes: +{candidate_doctypes} + +Rules: +- Choose only one DocType from the candidate list. +- Use only the given candidate DocTypes. +- Do not add the "tab" prefix. +- Return only valid JSON. +- Do not return explanations or extra text. +- If no suitable DocType is found, return {{"doctype": ""}} + +Output format: +{{"doctype": "chosen_doctype_name"}} \ No newline at end of file diff --git a/changai/changai/prompts/report_prompt.txt b/changai/changai/prompts/report_prompt.txt new file mode 100644 index 0000000..a0f464d --- /dev/null +++ b/changai/changai/prompts/report_prompt.txt @@ -0,0 +1,53 @@ +Given the user query: +{question} + +Detected DocType: +{doctype} + +Available Report and Filter Fields: +{available_reports} + +Entity Cards: +{entity_cards} + +Carefully identify the report intent from the user's question and match only that intent against the available report names. + +Report selection MUST be based on the user's requested report type first. +Entity type, party names, customer names, supplier names, item names, or filter values must be used only for filters, not for report_name selection. + +TASK: +Select exactly ONE report from the Available Reports and Filter Fields. + +Rules: +- report_name MUST be selected only from the provided report list. +- Never invent a report name. +- Never return an empty report_name. +- Use ONLY filters that exist in the selected report. +- Never invent filter names. +- Never invent filter values. +- Use Entity Card values whenever applicable. +- Apply only relevant filters supported by the selected report. +- Prefer party_type + party over name-based filters whenever both are available. +- Always output dates in YYYY-MM-DD format. +- Return only valid JSON. + +Party Filtering Rules: +If detected DocType is Customer, Supplier, Employee, Shareholder, Student, Member, or any party-type entity +AND selected report contains both party_type and party filters, +THEN: +- party_type = detected DocType +- party = entity value from Entity Card + +Examples: +User: "show account statement for nova supplier" +Report: General Ledger +Filters: +{{"party_type": "Supplier", "party": "nova supplier"}} + +Return ONLY valid JSON: +{{ + "report_name": "", + "filters": {{ + "": "" + }} +}} \ No newline at end of file diff --git a/changai/changai/prompts/retry_sys_prompt.txt b/changai/changai/prompts/retry_sys_prompt.txt new file mode 100644 index 0000000..bc2432c --- /dev/null +++ b/changai/changai/prompts/retry_sys_prompt.txt @@ -0,0 +1,8 @@ +═══ RETRY MODE — STRICT FIX REQUIRED ═══ +STEP 1: Read the failed SQL and error message. +STEP 2: Find the broken field/table. +STEP 3: Check SCHEMA CONTEXT — does it exist? + YES → fix the syntax. + NO → remove it, find correct field from SCHEMA CONTEXT. +STEP 4: Verify every remaining field exists in SCHEMA CONTEXT. +STEP 5: Output fixed SQL. NEVER output the same broken SQL again. \ No newline at end of file diff --git a/changai/changai/prompts/retry_user_prompt.txt b/changai/changai/prompts/retry_user_prompt.txt new file mode 100644 index 0000000..fde39d1 --- /dev/null +++ b/changai/changai/prompts/retry_user_prompt.txt @@ -0,0 +1,7 @@ +Failed SQL: {sql} +Error: {error} +User Question: {formatted_q} + +DO NOT repeat the same SQL. +DO NOT use the field mentioned in the error. +Find the correct field from SCHEMA CONTEXT and fix it. \ No newline at end of file diff --git a/changai/changai/prompts/sql_system_prompt.txt b/changai/changai/prompts/sql_system_prompt.txt index 03359a7..1753b5c 100644 --- a/changai/changai/prompts/sql_system_prompt.txt +++ b/changai/changai/prompts/sql_system_prompt.txt @@ -45,22 +45,31 @@ If a matching ENTITY_CARD is found for a user-mentioned entity, the SQL WHERE cl - the ENTITY_CARD filter_field - the ENTITY_CARD filter_value -═══ DOCSTATUS LAW ═══ -- docstatus ONLY exists on submittable transaction doctypes: - Sales Invoice, Sales Order, Purchase Order, Purchase Invoice, - Payment Entry, Journal Entry, Stock Entry, Delivery Note, - Purchase Receipt, Work Order, Timesheet, Expense Claim. -- docstatus NEVER exists on master doctypes: - Customer, Supplier, Item, Employee, Warehouse, Territory, - Address, Contact, User, Item Group, Customer Group. -- NEVER add docstatus to a master doctype query under any circumstance. -- Values: 0 = Draft | 1 = Submitted | 2 = Cancelled -- Any transactional document that goes through a Draft → Submit → Cancel cycle has a docstatus field. -- docstatus = 0 → ONLY when user explicitly says "draft" -- docstatus = 1 → ONLY when user explicitly says "submitted" -- docstatus = 2 → ONLY when user explicitly says "cancelled" -- NEVER use `status` field to represent Draft/Submitted/Cancelled states. docstatus has absolute priority. +═══ DOCSTATUS LAW — STRICT ═══ +docstatus is NOT a general validity filter. +Use docstatus ONLY when BOTH conditions are true: +1. The table is a known submittable transaction doctype. +2. The user explicitly asks for Draft, Submitted, Cancelled, posted, valid transaction, or confirmed transaction records. +Allowed transaction doctypes for docstatus: +Sales Invoice, Sales Order, Purchase Order, Purchase Invoice, Payment Entry, +Journal Entry, Stock Entry, Delivery Note, Purchase Receipt, Work Order, +Timesheet, Expense Claim. +Never use docstatus on master/setup doctypes: +Customer, Supplier, Item, Employee, Warehouse, Territory, Address, Contact, +User, Item Group, Customer Group, Company, Country, Currency, UOM, +Cost Center, Department, Project, Lead, Opportunity. + +docstatus values: +- draft → docstatus = 0 +- submitted / posted / confirmed / valid transaction → docstatus = 1 +- cancelled → docstatus = 2 +If the user does not mention draft/submitted/cancelled/posted/confirmed, do NOT add docstatus. +For list/count/top/grouping questions on master data, NEVER add docstatus. +- `status` and `docstatus` are NOT interchangeable. +- `docstatus` is ONLY for submittable transaction doctypes, never for master/setup doctypes. +- For Draft, Submitted, and Cancelled transaction records, ALWAYS use `docstatus`. +- NEVER use `status` as a replacement for `docstatus`. ═══ MARIADB COMPATIBILITY (ZERO TOLERANCE) ═══ FORBIDDEN — NEVER USE UNDER ANY CIRCUMSTANCE: STRFTIME, DATE_TRUNC, ::, ILIKE, TO_CHAR, NOW()::, EXTRACT, INTERVAL 'x' @@ -90,7 +99,6 @@ If you are about to write a forbidden token → STOP → rewrite using MariaDB e - Match the closest option to the user's intent. - If multiple options match, use IN (...). - pending invoice → outstanding_amount > 0 → unpaid invoice → overdue invoice. -- Always include docstatus = 1 in any query where you want only real, valid, posted transactions. ═══ SYNTAX LAW ═══ - Multiple values on same field → always IN (...), never chained OR. diff --git a/changai/changai/prompts/sql_user_prompt.txt b/changai/changai/prompts/sql_user_prompt.txt index 07666b2..93255e9 100644 --- a/changai/changai/prompts/sql_user_prompt.txt +++ b/changai/changai/prompts/sql_user_prompt.txt @@ -6,8 +6,10 @@ field_name:field_value When generating SQL for entity retrieval, you MUST use the exact field name and exact field value provided in the ENTITY_CARDS context for WHERE conditions. Do not use the raw user text or replace the field name with another field like `name`. SCHEMA CONTEXT: {context} -GENERIC FIELDS (available on ALL transaction doctypes): +GENERIC FIELDS are only available if the table type is correctly identified. +Transaction doctypes may have: name, creation, modified, owner, company, docstatus, naming_series, amended_from. -GENERIC FIELDS (available on ALL master doctypes): -name, creation, modified, owner, disabled, naming_series +Master/setup doctypes may have: +name, creation, modified, owner, disabled, naming_series. +Never transfer generic fields from transaction doctypes to master/setup doctypes. REMINDER: Use only fields from SCHEMA CONTEXT.and never ever use any field or any table that is not in the given schema.that is important.beware of using non existing fields. diff --git a/changai/hooks.py b/changai/hooks.py index 01d6ad2..b178468 100644 --- a/changai/hooks.py +++ b/changai/hooks.py @@ -170,21 +170,21 @@ # --------------- # Hook on document methods and events -# doc_events = { -# "Employee": { -# "on_update": "changai.changai.api.v2.create_qr.create_qr_code", +doc_events = { +"Employee": { + "on_update": "changai.changai.api.v2.create_qr.create_qr_code", -# }, + } # # "*": { # # "on_update": "method", # # "on_cancel": "method", # # "on_trash": "method" # # } -# } +} # also runs after bench migrate on_session_creation = [ - "changai.changai.api.v2.text2sql_pipeline_v2.load_on_startup", + "changai.changai.api.v2.retrieve.load_on_startup", "changai.changai.api.v2.schema_utils.reload_mapping_schema_cache" ] diff --git a/changai/public/dist/changai-chatbot.js b/changai/public/dist/changai-chatbot.js index 60ea2d3..7537fb6 100644 --- a/changai/public/dist/changai-chatbot.js +++ b/changai/public/dist/changai-chatbot.js @@ -1,69 +1,181 @@ -var vd=Object.defineProperty;var _d=(Pt,oe,mt)=>oe in Pt?vd(Pt,oe,{enumerable:!0,configurable:!0,writable:!0,value:mt}):Pt[oe]=mt;var _e=(Pt,oe,mt)=>_d(Pt,typeof oe!="symbol"?oe+"":oe,mt);(function(){"use strict";var Fs;function Pt(e){const t=Object.create(null);for(const n of e.split(","))t[n]=1;return n=>n in t}const oe={},mt=[],Jt=()=>{},Dl=()=>!1,ts=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Hs=e=>e.startsWith("onUpdate:"),bt=Object.assign,Jr=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Nl=Object.prototype.hasOwnProperty,me=(e,t)=>Nl.call(e,t),Q=Array.isArray,hn=e=>ns(e)==="[object Map]",ei=e=>ns(e)==="[object Set]",be=e=>typeof e=="function",Pe=e=>typeof e=="string",Ut=e=>typeof e=="symbol",Re=e=>e!==null&&typeof e=="object",ti=e=>(Re(e)||be(e))&&be(e.then)&&be(e.catch),ni=Object.prototype.toString,ns=e=>ni.call(e),Fl=e=>ns(e).slice(8,-1),si=e=>ns(e)==="[object Object]",zs=e=>Pe(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Pn=Pt(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),ss=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Bl=/-(\w)/g,jt=ss(e=>e.replace(Bl,(t,n)=>n?n.toUpperCase():"")),Hl=/\B([A-Z])/g,en=ss(e=>e.replace(Hl,"-$1").toLowerCase()),ri=ss(e=>e.charAt(0).toUpperCase()+e.slice(1)),Us=ss(e=>e?`on${ri(e)}`:""),Vt=(e,t)=>!Object.is(e,t),rs=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:s,value:n})},Vs=e=>{const t=parseFloat(e);return isNaN(t)?e:t},zl=e=>{const t=Pe(e)?Number(e):NaN;return isNaN(t)?e:t};let ii;const is=()=>ii||(ii=typeof globalThis!="undefined"?globalThis:typeof self!="undefined"?self:typeof window!="undefined"?window:typeof global!="undefined"?global:{});function qs(e){if(Q(e)){const t={};for(let n=0;n{if(n){const s=n.split(jl);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function we(e){let t="";if(Pe(e))t=e;else if(Q(e))for(let n=0;n!!(e&&e.__v_isRef===!0),Je=e=>Pe(e)?e:e==null?"":Q(e)||Re(e)&&(e.toString===ni||!be(e.toString))?li(e)?Je(e.value):JSON.stringify(e,ai,2):String(e),ai=(e,t)=>li(t)?ai(e,t.value):hn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r],i)=>(n[Ws(s,i)+" =>"]=r,n),{})}:ei(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>Ws(n))}:Ut(t)?Ws(t):Re(t)&&!Q(t)&&!si(t)?String(t):t,Ws=(e,t="")=>{var n;return Ut(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};let et;class Gl{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=et,!t&&et&&(this.index=(et.scopes||(et.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,n;if(this.scopes)for(t=0,n=this.scopes.length;t0&&--this._on===0&&(et=this.prevScope,this.prevScope=void 0)}stop(t){if(this._active){this._active=!1;let n,s;for(n=0,s=this.effects.length;n0)return;if(Mn){let t=Mn;for(Mn=void 0;t;){const n=t.next;t.next=void 0,t.flags&=-9,t=n}}let e;for(;In;){let t=In;for(In=void 0;t;){const n=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(s){e||(e=s)}t=n}}if(e)throw e}function pi(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function di(e){let t,n=e.depsTail,s=n;for(;s;){const r=s.prevDep;s.version===-1?(s===n&&(n=r),Xs(s),Yl(s)):t=s,s.dep.activeLink=s.prevActiveLink,s.prevActiveLink=void 0,s=r}e.deps=t,e.depsTail=n}function Zs(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(hi(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function hi(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===On)||(e.globalVersion=On,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Zs(e))))return;e.flags|=2;const t=e.dep,n=Te,s=xt;Te=e,xt=!0;try{pi(e);const r=e.fn(e._value);(t.version===0||Vt(r,e._value))&&(e.flags|=128,e._value=r,t.version++)}catch(r){throw t.version++,r}finally{Te=n,xt=s,di(e),e.flags&=-3}}function Xs(e,t=!1){const{dep:n,prevSub:s,nextSub:r}=e;if(s&&(s.nextSub=r,e.prevSub=void 0),r&&(r.prevSub=s,e.nextSub=void 0),n.subs===e&&(n.subs=s,!s&&n.computed)){n.computed.flags&=-5;for(let i=n.computed.deps;i;i=i.nextDep)Xs(i,!0)}!t&&!--n.sc&&n.map&&n.map.delete(n.key)}function Yl(e){const{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}let xt=!0;const gi=[];function It(){gi.push(xt),xt=!1}function Mt(){const e=gi.pop();xt=e===void 0?!0:e}function mi(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const n=Te;Te=void 0;try{t()}finally{Te=n}}}let On=0;class Zl{constructor(t,n){this.sub=t,this.dep=n,this.version=n.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class Qs{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!Te||!xt||Te===this.computed)return;let n=this.activeLink;if(n===void 0||n.sub!==Te)n=this.activeLink=new Zl(Te,this),Te.deps?(n.prevDep=Te.depsTail,Te.depsTail.nextDep=n,Te.depsTail=n):Te.deps=Te.depsTail=n,bi(n);else if(n.version===-1&&(n.version=this.version,n.nextDep)){const s=n.nextDep;s.prevDep=n.prevDep,n.prevDep&&(n.prevDep.nextDep=s),n.prevDep=Te.depsTail,n.nextDep=void 0,Te.depsTail.nextDep=n,Te.depsTail=n,Te.deps===n&&(Te.deps=s)}return n}trigger(t){this.version++,On++,this.notify(t)}notify(t){Ks();try{for(let n=this.subs;n;n=n.prevSub)n.sub.notify()&&n.sub.dep.notify()}finally{Ys()}}}function bi(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let s=t.deps;s;s=s.nextDep)bi(s)}const n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}const Js=new WeakMap,tn=Symbol(""),er=Symbol(""),Ln=Symbol("");function je(e,t,n){if(xt&&Te){let s=Js.get(e);s||Js.set(e,s=new Map);let r=s.get(n);r||(s.set(n,r=new Qs),r.map=s,r.key=n),r.track()}}function Ot(e,t,n,s,r,i){const o=Js.get(e);if(!o){On++;return}const a=l=>{l&&l.trigger()};if(Ks(),t==="clear")o.forEach(a);else{const l=Q(e),f=l&&zs(n);if(l&&n==="length"){const u=Number(s);o.forEach((d,g)=>{(g==="length"||g===Ln||!Ut(g)&&g>=u)&&a(d)})}else switch((n!==void 0||o.has(void 0))&&a(o.get(n)),f&&a(o.get(Ln)),t){case"add":l?f&&a(o.get("length")):(a(o.get(tn)),hn(e)&&a(o.get(er)));break;case"delete":l||(a(o.get(tn)),hn(e)&&a(o.get(er)));break;case"set":hn(e)&&a(o.get(tn));break}}Ys()}function gn(e){const t=ue(e);return t===e?t:(je(t,"iterate",Ln),ut(e)?t:t.map(Be))}function os(e){return je(e=ue(e),"iterate",Ln),e}const Xl={__proto__:null,[Symbol.iterator](){return tr(this,Symbol.iterator,Be)},concat(...e){return gn(this).concat(...e.map(t=>Q(t)?gn(t):t))},entries(){return tr(this,"entries",e=>(e[1]=Be(e[1]),e))},every(e,t){return Lt(this,"every",e,t,void 0,arguments)},filter(e,t){return Lt(this,"filter",e,t,n=>n.map(Be),arguments)},find(e,t){return Lt(this,"find",e,t,Be,arguments)},findIndex(e,t){return Lt(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return Lt(this,"findLast",e,t,Be,arguments)},findLastIndex(e,t){return Lt(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return Lt(this,"forEach",e,t,void 0,arguments)},includes(...e){return nr(this,"includes",e)},indexOf(...e){return nr(this,"indexOf",e)},join(e){return gn(this).join(e)},lastIndexOf(...e){return nr(this,"lastIndexOf",e)},map(e,t){return Lt(this,"map",e,t,void 0,arguments)},pop(){return $n(this,"pop")},push(...e){return $n(this,"push",e)},reduce(e,...t){return xi(this,"reduce",e,t)},reduceRight(e,...t){return xi(this,"reduceRight",e,t)},shift(){return $n(this,"shift")},some(e,t){return Lt(this,"some",e,t,void 0,arguments)},splice(...e){return $n(this,"splice",e)},toReversed(){return gn(this).toReversed()},toSorted(e){return gn(this).toSorted(e)},toSpliced(...e){return gn(this).toSpliced(...e)},unshift(...e){return $n(this,"unshift",e)},values(){return tr(this,"values",Be)}};function tr(e,t,n){const s=os(e),r=s[t]();return s!==e&&!ut(e)&&(r._next=r.next,r.next=()=>{const i=r._next();return i.value&&(i.value=n(i.value)),i}),r}const Ql=Array.prototype;function Lt(e,t,n,s,r,i){const o=os(e),a=o!==e&&!ut(e),l=o[t];if(l!==Ql[t]){const d=l.apply(e,i);return a?Be(d):d}let f=n;o!==e&&(a?f=function(d,g){return n.call(this,Be(d),g,e)}:n.length>2&&(f=function(d,g){return n.call(this,d,g,e)}));const u=l.call(o,f,s);return a&&r?r(u):u}function xi(e,t,n,s){const r=os(e);let i=n;return r!==e&&(ut(e)?n.length>3&&(i=function(o,a,l){return n.call(this,o,a,l,e)}):i=function(o,a,l){return n.call(this,o,Be(a),l,e)}),r[t](i,...s)}function nr(e,t,n){const s=ue(e);je(s,"iterate",Ln);const r=s[t](...n);return(r===-1||r===!1)&&rr(n[0])?(n[0]=ue(n[0]),s[t](...n)):r}function $n(e,t,n=[]){It(),Ks();const s=ue(e)[t].apply(e,n);return Ys(),Mt(),s}const Jl=Pt("__proto__,__v_isRef,__isVue"),yi=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Ut));function ea(e){Ut(e)||(e=String(e));const t=ue(this);return je(t,"has",e),t.hasOwnProperty(e)}class wi{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,s){if(n==="__v_skip")return t.__v_skip;const r=this._isReadonly,i=this._isShallow;if(n==="__v_isReactive")return!r;if(n==="__v_isReadonly")return r;if(n==="__v_isShallow")return i;if(n==="__v_raw")return s===(r?i?Ei:Si:i?ki:Ti).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(s)?t:void 0;const o=Q(t);if(!r){let l;if(o&&(l=Xl[n]))return l;if(n==="hasOwnProperty")return ea}const a=Reflect.get(t,n,Ve(t)?t:s);return(Ut(n)?yi.has(n):Jl(n))||(r||je(t,"get",n),i)?a:Ve(a)?o&&zs(n)?a:a.value:Re(a)?r?Ai(a):cs(a):a}}class vi extends wi{constructor(t=!1){super(!1,t)}set(t,n,s,r){let i=t[n];if(!this._isShallow){const l=qt(i);if(!ut(s)&&!qt(s)&&(i=ue(i),s=ue(s)),!Q(t)&&Ve(i)&&!Ve(s))return l?!1:(i.value=s,!0)}const o=Q(t)&&zs(n)?Number(n)e,ls=e=>Reflect.getPrototypeOf(e);function ia(e,t,n){return function(...s){const r=this.__v_raw,i=ue(r),o=hn(i),a=e==="entries"||e===Symbol.iterator&&o,l=e==="keys"&&o,f=r[e](...s),u=n?sr:t?ps:Be;return!t&&je(i,"iterate",l?er:tn),{next(){const{value:d,done:g}=f.next();return g?{value:d,done:g}:{value:a?[u(d[0]),u(d[1])]:u(d),done:g}},[Symbol.iterator](){return this}}}}function as(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function oa(e,t){const n={get(r){const i=this.__v_raw,o=ue(i),a=ue(r);e||(Vt(r,a)&&je(o,"get",r),je(o,"get",a));const{has:l}=ls(o),f=t?sr:e?ps:Be;if(l.call(o,r))return f(i.get(r));if(l.call(o,a))return f(i.get(a));i!==o&&i.get(r)},get size(){const r=this.__v_raw;return!e&&je(ue(r),"iterate",tn),Reflect.get(r,"size",r)},has(r){const i=this.__v_raw,o=ue(i),a=ue(r);return e||(Vt(r,a)&&je(o,"has",r),je(o,"has",a)),r===a?i.has(r):i.has(r)||i.has(a)},forEach(r,i){const o=this,a=o.__v_raw,l=ue(a),f=t?sr:e?ps:Be;return!e&&je(l,"iterate",tn),a.forEach((u,d)=>r.call(i,f(u),f(d),o))}};return bt(n,e?{add:as("add"),set:as("set"),delete:as("delete"),clear:as("clear")}:{add(r){!t&&!ut(r)&&!qt(r)&&(r=ue(r));const i=ue(this);return ls(i).has.call(i,r)||(i.add(r),Ot(i,"add",r,r)),this},set(r,i){!t&&!ut(i)&&!qt(i)&&(i=ue(i));const o=ue(this),{has:a,get:l}=ls(o);let f=a.call(o,r);f||(r=ue(r),f=a.call(o,r));const u=l.call(o,r);return o.set(r,i),f?Vt(i,u)&&Ot(o,"set",r,i):Ot(o,"add",r,i),this},delete(r){const i=ue(this),{has:o,get:a}=ls(i);let l=o.call(i,r);l||(r=ue(r),l=o.call(i,r)),a&&a.call(i,r);const f=i.delete(r);return l&&Ot(i,"delete",r,void 0),f},clear(){const r=ue(this),i=r.size!==0,o=r.clear();return i&&Ot(r,"clear",void 0,void 0),o}}),["keys","values","entries",Symbol.iterator].forEach(r=>{n[r]=ia(r,e,t)}),n}function us(e,t){const n=oa(e,t);return(s,r,i)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(me(n,r)&&r in s?n:s,r,i)}const la={get:us(!1,!1)},aa={get:us(!1,!0)},ua={get:us(!0,!1)},ca={get:us(!0,!0)},Ti=new WeakMap,ki=new WeakMap,Si=new WeakMap,Ei=new WeakMap;function fa(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function pa(e){return e.__v_skip||!Object.isExtensible(e)?0:fa(Fl(e))}function cs(e){return qt(e)?e:fs(e,!1,ta,la,Ti)}function da(e){return fs(e,!1,sa,aa,ki)}function Ai(e){return fs(e,!0,na,ua,Si)}function kd(e){return fs(e,!0,ra,ca,Ei)}function fs(e,t,n,s,r){if(!Re(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=pa(e);if(i===0)return e;const o=r.get(e);if(o)return o;const a=new Proxy(e,i===2?s:n);return r.set(e,a),a}function mn(e){return qt(e)?mn(e.__v_raw):!!(e&&e.__v_isReactive)}function qt(e){return!!(e&&e.__v_isReadonly)}function ut(e){return!!(e&&e.__v_isShallow)}function rr(e){return e?!!e.__v_raw:!1}function ue(e){const t=e&&e.__v_raw;return t?ue(t):e}function ha(e){return!me(e,"__v_skip")&&Object.isExtensible(e)&&js(e,"__v_skip",!0),e}const Be=e=>Re(e)?cs(e):e,ps=e=>Re(e)?Ai(e):e;function Ve(e){return e?e.__v_isRef===!0:!1}function J(e){return ga(e,!1)}function ga(e,t){return Ve(e)?e:new ma(e,t)}class ma{constructor(t,n){this.dep=new Qs,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=n?t:ue(t),this._value=n?t:Be(t),this.__v_isShallow=n}get value(){return this.dep.track(),this._value}set value(t){const n=this._rawValue,s=this.__v_isShallow||ut(t)||qt(t);t=s?t:ue(t),Vt(t,n)&&(this._rawValue=t,this._value=s?t:Be(t),this.dep.trigger())}}function ba(e){return Ve(e)?e.value:e}const xa={get:(e,t,n)=>t==="__v_raw"?e:ba(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return Ve(r)&&!Ve(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function Ri(e){return mn(e)?e:new Proxy(e,xa)}class ya{constructor(t,n,s){this.fn=t,this.setter=n,this._value=void 0,this.dep=new Qs(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=On-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!n,this.isSSR=s}notify(){if(this.flags|=16,!(this.flags&8)&&Te!==this)return fi(this,!0),!0}get value(){const t=this.dep.track();return hi(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function wa(e,t,n=!1){let s,r;return be(e)?s=e:(s=e.get,r=e.set),new ya(s,r,n)}const ds={},hs=new WeakMap;let nn;function va(e,t=!1,n=nn){if(n){let s=hs.get(n);s||hs.set(n,s=[]),s.push(e)}}function _a(e,t,n=oe){const{immediate:s,deep:r,once:i,scheduler:o,augmentJob:a,call:l}=n,f=$=>r?$:ut($)||r===!1||r===0?$t($,1):$t($);let u,d,g,y,E=!1,x=!1;if(Ve(e)?(d=()=>e.value,E=ut(e)):mn(e)?(d=()=>f(e),E=!0):Q(e)?(x=!0,E=e.some($=>mn($)||ut($)),d=()=>e.map($=>{if(Ve($))return $.value;if(mn($))return f($);if(be($))return l?l($,2):$()})):be(e)?t?d=l?()=>l(e,2):e:d=()=>{if(g){It();try{g()}finally{Mt()}}const $=nn;nn=u;try{return l?l(e,3,[y]):e(y)}finally{nn=$}}:d=Jt,t&&r){const $=d,z=r===!0?1/0:r;d=()=>$t($(),z)}const M=Kl(),m=()=>{u.stop(),M&&M.active&&Jr(M.effects,u)};if(i&&t){const $=t;t=(...z)=>{$(...z),m()}}let H=x?new Array(e.length).fill(ds):ds;const Y=$=>{if(!(!(u.flags&1)||!u.dirty&&!$))if(t){const z=u.run();if(r||E||(x?z.some((O,P)=>Vt(O,H[P])):Vt(z,H))){g&&g();const O=nn;nn=u;try{const P=[z,H===ds?void 0:x&&H[0]===ds?[]:H,y];H=z,l?l(t,3,P):t(...P)}finally{nn=O}}}else u.run()};return a&&a(Y),u=new ui(d),u.scheduler=o?()=>o(Y,!1):Y,y=$=>va($,!1,u),g=u.onStop=()=>{const $=hs.get(u);if($){if(l)l($,4);else for(const z of $)z();hs.delete(u)}},t?s?Y(!0):H=u.run():o?o(Y.bind(null,!0),!0):u.run(),m.pause=u.pause.bind(u),m.resume=u.resume.bind(u),m.stop=m,m}function $t(e,t=1/0,n){if(t<=0||!Re(e)||e.__v_skip||(n=n||new Set,n.has(e)))return e;if(n.add(e),t--,Ve(e))$t(e.value,t,n);else if(Q(e))for(let s=0;s{$t(s,t,n)});else if(si(e)){for(const s in e)$t(e[s],t,n);for(const s of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,s)&&$t(e[s],t,n)}return e}const Dn=[];let ir=!1;function Sd(e,...t){if(ir)return;ir=!0,It();const n=Dn.length?Dn[Dn.length-1].component:null,s=n&&n.appContext.config.warnHandler,r=Ta();if(s)bn(s,n,11,[e+t.map(i=>{var o,a;return(a=(o=i.toString)==null?void 0:o.call(i))!=null?a:JSON.stringify(i)}).join(""),n&&n.proxy,r.map(({vnode:i})=>`at <${go(n,i.type)}>`).join(` -`),r]);else{const i=[`[Vue warn]: ${e}`,...t];r.length&&i.push(` -`,...ka(r))}Mt(),ir=!1}function Ta(){let e=Dn[Dn.length-1];if(!e)return[];const t=[];for(;e;){const n=t[0];n&&n.vnode===e?n.recurseCount++:t.push({vnode:e,recurseCount:0});const s=e.component&&e.component.parent;e=s&&s.vnode}return t}function ka(e){const t=[];return e.forEach((n,s)=>{t.push(...s===0?[]:[` -`],...Sa(n))}),t}function Sa({vnode:e,recurseCount:t}){const n=t>0?`... (${t} recursive calls)`:"",s=e.component?e.component.parent==null:!1,r=` at <${go(e.component,e.type,s)}`,i=">"+n;return e.props?[r,...Ea(e.props),i]:[r+i]}function Ea(e){const t=[],n=Object.keys(e);return n.slice(0,3).forEach(s=>{t.push(...Ci(s,e[s]))}),n.length>3&&t.push(" ..."),t}function Ci(e,t,n){return Pe(t)?(t=JSON.stringify(t),n?t:[`${e}=${t}`]):typeof t=="number"||typeof t=="boolean"||t==null?n?t:[`${e}=${t}`]:Ve(t)?(t=Ci(e,ue(t.value),!0),n?t:[`${e}=Ref<`,t,">"]):be(t)?[`${e}=fn${t.name?`<${t.name}>`:""}`]:(t=ue(t),n?t:[`${e}=`,t])}function bn(e,t,n,s){try{return s?e(...s):e()}catch(r){gs(r,t,n)}}function Tt(e,t,n,s){if(be(e)){const r=bn(e,t,n,s);return r&&ti(r)&&r.catch(i=>{gs(i,t,n)}),r}if(Q(e)){const r=[];for(let i=0;i>>1,r=Ke[s],i=Nn(r);i=Nn(n)?Ke.push(e):Ke.splice(Ra(t),0,e),e.flags|=1,Ii()}}function Ii(){ms||(ms=Pi.then(Li))}function Ca(e){Q(e)?xn.push(...e):Wt&&e.id===-1?Wt.splice(yn+1,0,e):e.flags&1||(xn.push(e),e.flags|=1),Ii()}function Mi(e,t,n=kt+1){for(;nNn(n)-Nn(s));if(xn.length=0,Wt){Wt.push(...t);return}for(Wt=t,yn=0;yne.id==null?e.flags&2?-1:1/0:e.id;function Li(e){try{for(kt=0;kt{s._d&&oo(-1);const i=bs(t);let o;try{o=e(...r)}finally{bs(i),s._d&&oo(1)}return o};return s._n=!0,s._c=!0,s._d=!0,s}function Pa(e,t){if(ct===null)return e;const n=As(ct),s=e.dirs||(e.dirs=[]);for(let r=0;re.__isTeleport,Gt=Symbol("_leaveCb"),xs=Symbol("_enterCb");function Ma(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return zn(()=>{e.isMounted=!0}),ys(()=>{e.isUnmounting=!0}),e}const ft=[Function,Array],Fi={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:ft,onEnter:ft,onAfterEnter:ft,onEnterCancelled:ft,onBeforeLeave:ft,onLeave:ft,onAfterLeave:ft,onLeaveCancelled:ft,onBeforeAppear:ft,onAppear:ft,onAfterAppear:ft,onAppearCancelled:ft},Bi=e=>{const t=e.subTree;return t.component?Bi(t.component):t},Oa={name:"BaseTransition",props:Fi,setup(e,{slots:t}){const n=uo(),s=Ma();return()=>{const r=t.default&&ji(t.default(),!0);if(!r||!r.length)return;const i=Hi(r),o=ue(e),{mode:a}=o;if(s.isLeaving)return ar(i);const l=Ui(i);if(!l)return ar(i);let f=lr(l,o,s,n,d=>f=d);l.type!==Ye&&Fn(l,f);let u=n.subTree&&Ui(n.subTree);if(u&&u.type!==Ye&&!ln(l,u)&&Bi(n).type!==Ye){let d=lr(u,o,s,n);if(Fn(u,d),a==="out-in"&&l.type!==Ye)return s.isLeaving=!0,d.afterLeave=()=>{s.isLeaving=!1,n.job.flags&8||n.update(),delete d.afterLeave,u=void 0},ar(i);a==="in-out"&&l.type!==Ye?d.delayLeave=(g,y,E)=>{const x=zi(s,u);x[String(u.key)]=u,g[Gt]=()=>{y(),g[Gt]=void 0,delete f.delayedLeave,u=void 0},f.delayedLeave=()=>{E(),delete f.delayedLeave,u=void 0}}:u=void 0}else u&&(u=void 0);return i}}};function Hi(e){let t=e[0];if(e.length>1){for(const n of e)if(n.type!==Ye){t=n;break}}return t}const La=Oa;function zi(e,t){const{leavingVNodes:n}=e;let s=n.get(t.type);return s||(s=Object.create(null),n.set(t.type,s)),s}function lr(e,t,n,s,r){const{appear:i,mode:o,persisted:a=!1,onBeforeEnter:l,onEnter:f,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:g,onLeave:y,onAfterLeave:E,onLeaveCancelled:x,onBeforeAppear:M,onAppear:m,onAfterAppear:H,onAppearCancelled:Y}=t,$=String(e.key),z=zi(n,e),O=(V,se)=>{V&&Tt(V,s,9,se)},P=(V,se)=>{const fe=se[1];O(V,se),Q(V)?V.every(D=>D.length<=1)&&fe():V.length<=1&&fe()},ee={mode:o,persisted:a,beforeEnter(V){let se=l;if(!n.isMounted)if(i)se=M||l;else return;V[Gt]&&V[Gt](!0);const fe=z[$];fe&&ln(e,fe)&&fe.el[Gt]&&fe.el[Gt](),O(se,[V])},enter(V){let se=f,fe=u,D=d;if(!n.isMounted)if(i)se=m||f,fe=H||u,D=Y||d;else return;let Z=!1;const Se=V[xs]=Ue=>{Z||(Z=!0,Ue?O(D,[V]):O(fe,[V]),ee.delayedLeave&&ee.delayedLeave(),V[xs]=void 0)};se?P(se,[V,Se]):Se()},leave(V,se){const fe=String(e.key);if(V[xs]&&V[xs](!0),n.isUnmounting)return se();O(g,[V]);let D=!1;const Z=V[Gt]=Se=>{D||(D=!0,se(),Se?O(x,[V]):O(E,[V]),V[Gt]=void 0,z[fe]===e&&delete z[fe])};z[fe]=e,y?P(y,[V,Z]):Z()},clone(V){const se=lr(V,t,n,s,r);return r&&r(se),se}};return ee}function ar(e){if(ur(e))return e=Kt(e),e.children=null,e}function Ui(e){if(!ur(e))return Ni(e.type)&&e.children?Hi(e.children):e;if(e.component)return e.component.subTree;const{shapeFlag:t,children:n}=e;if(n){if(t&16)return n[0];if(t&32&&be(n.default))return n.default()}}function Fn(e,t){e.shapeFlag&6&&e.component?(e.transition=t,Fn(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function ji(e,t=!1,n){let s=[],r=0;for(let i=0;i1)for(let i=0;iBn(E,t&&(Q(t)?t[x]:t),n,s,r));return}if(Hn(s)&&!r){s.shapeFlag&512&&s.type.__asyncResolved&&s.component.subTree.component&&Bn(e,t,n,s.component.subTree);return}const i=s.shapeFlag&4?As(s.component):s.el,o=r?null:i,{i:a,r:l}=e,f=t&&t.r,u=a.refs===oe?a.refs={}:a.refs,d=a.setupState,g=ue(d),y=d===oe?()=>!1:E=>me(g,E);if(f!=null&&f!==l&&(Pe(f)?(u[f]=null,y(f)&&(d[f]=null)):Ve(f)&&(f.value=null)),be(l))bn(l,a,12,[o,u]);else{const E=Pe(l),x=Ve(l);if(E||x){const M=()=>{if(e.f){const m=E?y(l)?d[l]:u[l]:l.value;r?Q(m)&&Jr(m,i):Q(m)?m.includes(i)||m.push(i):E?(u[l]=[i],y(l)&&(d[l]=u[l])):(l.value=[i],e.k&&(u[e.k]=l.value))}else E?(u[l]=o,y(l)&&(d[l]=o)):x&&(l.value=o,e.k&&(u[e.k]=o))};o?(M.id=-1,rt(M,n)):M()}}}is().requestIdleCallback,is().cancelIdleCallback;const Hn=e=>!!e.type.__asyncLoader,ur=e=>e.type.__isKeepAlive;function Da(e,t,n=Zt,s=!1){if(n){const r=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...o)=>{It();const a=yr(n),l=Tt(t,n,e,o);return a(),Mt(),l});return s?r.unshift(i):r.push(i),i}}const Vi=e=>(t,n=Zt)=>{(!qn||e==="sp")&&Da(e,(...s)=>t(...s),n)},zn=Vi("m"),ys=Vi("bum"),Na=Symbol.for("v-ndc");function ws(e,t,n,s){let r;const i=n,o=Q(e);if(o||Pe(e)){const a=o&&mn(e);let l=!1,f=!1;a&&(l=!ut(e),f=qt(e),e=os(e)),r=new Array(e.length);for(let u=0,d=e.length;ut(a,l,void 0,i));else{const a=Object.keys(e);r=new Array(a.length);for(let l=0,f=a.length;le?fo(e)?As(e):cr(e.parent):null,Un=bt(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>cr(e.parent),$root:e=>cr(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>e.type,$forceUpdate:e=>e.f||(e.f=()=>{or(e.update)}),$nextTick:e=>e.n||(e.n=St.bind(e.proxy)),$watch:e=>Jt}),fr=(e,t)=>e!==oe&&!e.__isScriptSetup&&me(e,t),Fa={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:n,setupState:s,data:r,props:i,accessCache:o,type:a,appContext:l}=e;let f;if(t[0]!=="$"){const y=o[t];if(y!==void 0)switch(y){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return i[t]}else{if(fr(s,t))return o[t]=1,s[t];if(r!==oe&&me(r,t))return o[t]=2,r[t];if((f=e.propsOptions[0])&&me(f,t))return o[t]=3,i[t];if(n!==oe&&me(n,t))return o[t]=4,n[t];o[t]=0}}const u=Un[t];let d,g;if(u)return t==="$attrs"&&je(e.attrs,"get",""),u(e);if((d=a.__cssModules)&&(d=d[t]))return d;if(n!==oe&&me(n,t))return o[t]=4,n[t];if(g=l.config.globalProperties,me(g,t))return g[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:i}=e;return fr(r,t)?(r[t]=n,!0):s!==oe&&me(s,t)?(s[t]=n,!0):me(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:i}},o){let a;return!!n[o]||e!==oe&&me(e,o)||fr(t,o)||(a=i[0])&&me(a,o)||me(s,o)||me(Un,o)||me(r.config.globalProperties,o)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:me(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function qi(){return{app:null,config:{isNativeTag:Dl,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let Ba=0;function Ha(e,t){return function(s,r=null){be(s)||(s=bt({},s)),r!=null&&!Re(r)&&(r=null);const i=qi(),o=new WeakSet,a=[];let l=!1;const f=i.app={_uid:Ba++,_component:s,_props:r,_container:null,_context:i,_instance:null,version:Su,get config(){return i.config},set config(u){},use(u,...d){return o.has(u)||(u&&be(u.install)?(o.add(u),u.install(f,...d)):be(u)&&(o.add(u),u(f,...d))),f},mixin(u){return f},component(u,d){return d?(i.components[u]=d,f):i.components[u]},directive(u,d){return d?(i.directives[u]=d,f):i.directives[u]},mount(u,d,g){if(!l){const y=f._ceVNode||Oe(s,r);return y.appContext=i,g===!0?g="svg":g===!1&&(g=void 0),e(y,u,g),l=!0,f._container=u,u.__vue_app__=f,As(y.component)}},onUnmount(u){a.push(u)},unmount(){l&&(Tt(a,f._instance,16),e(null,f._container),delete f._container.__vue_app__)},provide(u,d){return i.provides[u]=d,f},runWithContext(u){const d=wn;wn=f;try{return u()}finally{wn=d}}};return f}}let wn=null;function za(e,t,n=!1){const s=uo();if(s||wn){let r=wn?wn._context.provides:s?s.parent==null||s.ce?s.vnode.appContext&&s.vnode.appContext.provides:s.parent.provides:void 0;if(r&&e in r)return r[e];if(arguments.length>1)return n&&be(t)?t.call(s&&s.proxy):t}}const Wi={},Gi=()=>Object.create(Wi),Ki=e=>Object.getPrototypeOf(e)===Wi;function Ua(e,t,n,s=!1){const r={},i=Gi();e.propsDefaults=Object.create(null),Yi(e,t,r,i);for(const o in e.propsOptions[0])o in r||(r[o]=void 0);n?e.props=s?r:da(r):e.type.props?e.props=r:e.props=i,e.attrs=i}function ja(e,t,n,s){const{props:r,attrs:i,vnode:{patchFlag:o}}=e,a=ue(r),[l]=e.propsOptions;let f=!1;if((s||o>0)&&!(o&16)){if(o&8){const u=e.vnode.dynamicProps;for(let d=0;de==="_"||e==="__"||e==="_ctx"||e==="$stable",hr=e=>Q(e)?e.map(At):[At(e)],qa=(e,t,n)=>{if(t._n)return t;const s=Di((...r)=>hr(t(...r)),n);return s._c=!1,s},Xi=(e,t,n)=>{const s=e._ctx;for(const r in e){if(dr(r))continue;const i=e[r];if(be(i))t[r]=qa(r,i,s);else if(i!=null){const o=hr(i);t[r]=()=>o}}},Qi=(e,t)=>{const n=hr(t);e.slots.default=()=>n},Ji=(e,t,n)=>{for(const s in t)(n||!dr(s))&&(e[s]=t[s])},Wa=(e,t,n)=>{const s=e.slots=Gi();if(e.vnode.shapeFlag&32){const r=t.__;r&&js(s,"__",r,!0);const i=t._;i?(Ji(s,t,n),n&&js(s,"_",i,!0)):Xi(t,s)}else t&&Qi(e,t)},Ga=(e,t,n)=>{const{vnode:s,slots:r}=e;let i=!0,o=oe;if(s.shapeFlag&32){const a=t._;a?n&&a===1?i=!1:Ji(r,t,n):(i=!t.$stable,Xi(t,r)),o=t}else t&&(Qi(e,t),o={default:1});if(i)for(const a in r)!dr(a)&&o[a]==null&&delete r[a]},rt=au;function Ka(e){return Ya(e)}function Ya(e,t){const n=is();n.__VUE__=!0;const{insert:s,remove:r,patchProp:i,createElement:o,createText:a,createComment:l,setText:f,setElementText:u,parentNode:d,nextSibling:g,setScopeId:y=Jt,insertStaticContent:E}=e,x=(c,h,b,k=null,v=null,_=null,I=void 0,R=null,A=!!h.dynamicChildren)=>{if(c===h)return;c&&!ln(c,h)&&(k=ye(c),de(c,v,_,!0),c=null),h.patchFlag===-2&&(A=!1,h.dynamicChildren=null);const{type:S,ref:U,shapeFlag:L}=h;switch(S){case _s:M(c,h,b,k);break;case Ye:m(c,h,b,k);break;case mr:c==null&&H(h,b,k,I);break;case He:D(c,h,b,k,v,_,I,R,A);break;default:L&1?z(c,h,b,k,v,_,I,R,A):L&6?Z(c,h,b,k,v,_,I,R,A):(L&64||L&128)&&S.process(c,h,b,k,v,_,I,R,A,Ce)}U!=null&&v?Bn(U,c&&c.ref,_,h||c,!h):U==null&&c&&c.ref!=null&&Bn(c.ref,null,_,c,!0)},M=(c,h,b,k)=>{if(c==null)s(h.el=a(h.children),b,k);else{const v=h.el=c.el;h.children!==c.children&&f(v,h.children)}},m=(c,h,b,k)=>{c==null?s(h.el=l(h.children||""),b,k):h.el=c.el},H=(c,h,b,k)=>{[c.el,c.anchor]=E(c.children,h,b,k,c.el,c.anchor)},Y=({el:c,anchor:h},b,k)=>{let v;for(;c&&c!==h;)v=g(c),s(c,b,k),c=v;s(h,b,k)},$=({el:c,anchor:h})=>{let b;for(;c&&c!==h;)b=g(c),r(c),c=b;r(h)},z=(c,h,b,k,v,_,I,R,A)=>{h.type==="svg"?I="svg":h.type==="math"&&(I="mathml"),c==null?O(h,b,k,v,_,I,R,A):V(c,h,v,_,I,R,A)},O=(c,h,b,k,v,_,I,R)=>{let A,S;const{props:U,shapeFlag:L,transition:j,dirs:K}=c;if(A=c.el=o(c.type,_,U&&U.is,U),L&8?u(A,c.children):L&16&&ee(c.children,A,null,k,v,gr(c,_),I,R),K&&sn(c,null,k,"created"),P(A,c,c.scopeId,I,k),U){for(const he in U)he!=="value"&&!Pn(he)&&i(A,he,null,U[he],_,k);"value"in U&&i(A,"value",null,U.value,_),(S=U.onVnodeBeforeMount)&&Rt(S,k,c)}K&&sn(c,null,k,"beforeMount");const ne=Za(v,j);ne&&j.beforeEnter(A),s(A,h,b),((S=U&&U.onVnodeMounted)||ne||K)&&rt(()=>{S&&Rt(S,k,c),ne&&j.enter(A),K&&sn(c,null,k,"mounted")},v)},P=(c,h,b,k,v)=>{if(b&&y(c,b),k)for(let _=0;_{for(let S=A;S{const R=h.el=c.el;let{patchFlag:A,dynamicChildren:S,dirs:U}=h;A|=c.patchFlag&16;const L=c.props||oe,j=h.props||oe;let K;if(b&&rn(b,!1),(K=j.onVnodeBeforeUpdate)&&Rt(K,b,h,c),U&&sn(h,c,b,"beforeUpdate"),b&&rn(b,!0),(L.innerHTML&&j.innerHTML==null||L.textContent&&j.textContent==null)&&u(R,""),S?se(c.dynamicChildren,S,R,b,k,gr(h,v),_):I||pe(c,h,R,null,b,k,gr(h,v),_,!1),A>0){if(A&16)fe(R,L,j,b,v);else if(A&2&&L.class!==j.class&&i(R,"class",null,j.class,v),A&4&&i(R,"style",L.style,j.style,v),A&8){const ne=h.dynamicProps;for(let he=0;he{K&&Rt(K,b,h,c),U&&sn(h,c,b,"updated")},k)},se=(c,h,b,k,v,_,I)=>{for(let R=0;R{if(h!==b){if(h!==oe)for(const _ in h)!Pn(_)&&!(_ in b)&&i(c,_,h[_],null,v,k);for(const _ in b){if(Pn(_))continue;const I=b[_],R=h[_];I!==R&&_!=="value"&&i(c,_,R,I,v,k)}"value"in b&&i(c,"value",h.value,b.value,v)}},D=(c,h,b,k,v,_,I,R,A)=>{const S=h.el=c?c.el:a(""),U=h.anchor=c?c.anchor:a("");let{patchFlag:L,dynamicChildren:j,slotScopeIds:K}=h;K&&(R=R?R.concat(K):K),c==null?(s(S,b,k),s(U,b,k),ee(h.children||[],b,U,v,_,I,R,A)):L>0&&L&64&&j&&c.dynamicChildren?(se(c.dynamicChildren,j,b,v,_,I,R),(h.key!=null||v&&h===v.subTree)&&eo(c,h,!0)):pe(c,h,b,U,v,_,I,R,A)},Z=(c,h,b,k,v,_,I,R,A)=>{h.slotScopeIds=R,c==null?h.shapeFlag&512?v.ctx.activate(h,b,k,I,A):Se(h,b,k,v,_,I,A):Ue(c,h,A)},Se=(c,h,b,k,v,_,I)=>{const R=c.component=gu(c,k,v);if(ur(c)&&(R.ctx.renderer=Ce),mu(R,!1,I),R.asyncDep){if(v&&v.registerDep(R,te,I),!c.el){const A=R.subTree=Oe(Ye);m(null,A,h,b),c.placeholder=A.el}}else te(R,c,h,b,v,_,I)},Ue=(c,h,b)=>{const k=h.component=c.component;if(ou(c,h,b))if(k.asyncDep&&!k.asyncResolved){C(k,h,b);return}else k.next=h,k.update();else h.el=c.el,k.vnode=h},te=(c,h,b,k,v,_,I)=>{const R=()=>{if(c.isMounted){let{next:L,bu:j,u:K,parent:ne,vnode:he}=c;{const lt=to(c);if(lt){L&&(L.el=he.el,C(c,L,I)),lt.asyncDep.then(()=>{c.isUnmounted||R()});return}}let ie=L,ke;rn(c,!1),L?(L.el=he.el,C(c,L,I)):L=he,j&&rs(j),(ke=L.props&&L.props.onVnodeBeforeUpdate)&&Rt(ke,ne,L,he),rn(c,!0);const $e=so(c),ot=c.subTree;c.subTree=$e,x(ot,$e,d(ot.el),ye(ot),c,v,_),L.el=$e.el,ie===null&&lu(c,$e.el),K&&rt(K,v),(ke=L.props&&L.props.onVnodeUpdated)&&rt(()=>Rt(ke,ne,L,he),v)}else{let L;const{el:j,props:K}=h,{bm:ne,m:he,parent:ie,root:ke,type:$e}=c,ot=Hn(h);rn(c,!1),ne&&rs(ne),!ot&&(L=K&&K.onVnodeBeforeMount)&&Rt(L,ie,h),rn(c,!0);{ke.ce&&ke.ce._def.shadowRoot!==!1&&ke.ce._injectChildStyle($e);const lt=c.subTree=so(c);x(null,lt,b,k,c,v,_),h.el=lt.el}if(he&&rt(he,v),!ot&&(L=K&&K.onVnodeMounted)){const lt=h;rt(()=>Rt(L,ie,lt),v)}(h.shapeFlag&256||ie&&Hn(ie.vnode)&&ie.vnode.shapeFlag&256)&&c.a&&rt(c.a,v),c.isMounted=!0,h=b=k=null}};c.scope.on();const A=c.effect=new ui(R);c.scope.off();const S=c.update=A.run.bind(A),U=c.job=A.runIfDirty.bind(A);U.i=c,U.id=c.uid,A.scheduler=()=>or(U),rn(c,!0),S()},C=(c,h,b)=>{h.component=c;const k=c.vnode.props;c.vnode=h,c.next=null,ja(c,h.props,k,b),Ga(c,h.children,b),It(),Mi(c),Mt()},pe=(c,h,b,k,v,_,I,R,A=!1)=>{const S=c&&c.children,U=c?c.shapeFlag:0,L=h.children,{patchFlag:j,shapeFlag:K}=h;if(j>0){if(j&128){re(S,L,b,k,v,_,I,R,A);return}else if(j&256){Ge(S,L,b,k,v,_,I,R,A);return}}K&8?(U&16&&nt(S,v,_),L!==S&&u(b,L)):U&16?K&16?re(S,L,b,k,v,_,I,R,A):nt(S,v,_,!0):(U&8&&u(b,""),K&16&&ee(L,b,k,v,_,I,R,A))},Ge=(c,h,b,k,v,_,I,R,A)=>{c=c||mt,h=h||mt;const S=c.length,U=h.length,L=Math.min(S,U);let j;for(j=0;jU?nt(c,v,_,!0,!1,L):ee(h,b,k,v,_,I,R,A,L)},re=(c,h,b,k,v,_,I,R,A)=>{let S=0;const U=h.length;let L=c.length-1,j=U-1;for(;S<=L&&S<=j;){const K=c[S],ne=h[S]=A?Yt(h[S]):At(h[S]);if(ln(K,ne))x(K,ne,b,null,v,_,I,R,A);else break;S++}for(;S<=L&&S<=j;){const K=c[L],ne=h[j]=A?Yt(h[j]):At(h[j]);if(ln(K,ne))x(K,ne,b,null,v,_,I,R,A);else break;L--,j--}if(S>L){if(S<=j){const K=j+1,ne=Kj)for(;S<=L;)de(c[S],v,_,!0),S++;else{const K=S,ne=S,he=new Map;for(S=ne;S<=j;S++){const De=h[S]=A?Yt(h[S]):At(h[S]);De.key!=null&&he.set(De.key,S)}let ie,ke=0;const $e=j-ne+1;let ot=!1,lt=0;const Bt=new Array($e);for(S=0;S<$e;S++)Bt[S]=0;for(S=K;S<=L;S++){const De=c[S];if(ke>=$e){de(De,v,_,!0);continue}let Qe;if(De.key!=null)Qe=he.get(De.key);else for(ie=ne;ie<=j;ie++)if(Bt[ie-ne]===0&&ln(De,h[ie])){Qe=ie;break}Qe===void 0?de(De,v,_,!0):(Bt[Qe-ne]=S+1,Qe>=lt?lt=Qe:ot=!0,x(De,h[Qe],b,null,v,_,I,R,A),ke++)}const Jn=ot?Xa(Bt):mt;for(ie=Jn.length-1,S=$e-1;S>=0;S--){const De=ne+S,Qe=h[De],at=h[De+1],Ht=De+1{const{el:_,type:I,transition:R,children:A,shapeFlag:S}=c;if(S&6){B(c.component.subTree,h,b,k);return}if(S&128){c.suspense.move(h,b,k);return}if(S&64){I.move(c,h,b,Ce);return}if(I===He){s(_,h,b);for(let L=0;LR.enter(_),v);else{const{leave:L,delayLeave:j,afterLeave:K}=R,ne=()=>{c.ctx.isUnmounted?r(_):s(_,h,b)},he=()=>{L(_,()=>{ne(),K&&K()})};j?j(_,ne,he):he()}else s(_,h,b)},de=(c,h,b,k=!1,v=!1)=>{const{type:_,props:I,ref:R,children:A,dynamicChildren:S,shapeFlag:U,patchFlag:L,dirs:j,cacheIndex:K}=c;if(L===-2&&(v=!1),R!=null&&(It(),Bn(R,null,b,c,!0),Mt()),K!=null&&(h.renderCache[K]=void 0),U&256){h.ctx.deactivate(c);return}const ne=U&1&&j,he=!Hn(c);let ie;if(he&&(ie=I&&I.onVnodeBeforeUnmount)&&Rt(ie,h,c),U&6)ge(c.component,b,k);else{if(U&128){c.suspense.unmount(b,k);return}ne&&sn(c,null,h,"beforeUnmount"),U&64?c.type.remove(c,h,b,Ce,k):S&&!S.hasOnce&&(_!==He||L>0&&L&64)?nt(S,h,b,!1,!0):(_===He&&L&384||!v&&U&16)&&nt(A,h,b),k&&ae(c)}(he&&(ie=I&&I.onVnodeUnmounted)||ne)&&rt(()=>{ie&&Rt(ie,h,c),ne&&sn(c,null,h,"unmounted")},b)},ae=c=>{const{type:h,el:b,anchor:k,transition:v}=c;if(h===He){ve(b,k);return}if(h===mr){$(c);return}const _=()=>{r(b),v&&!v.persisted&&v.afterLeave&&v.afterLeave()};if(c.shapeFlag&1&&v&&!v.persisted){const{leave:I,delayLeave:R}=v,A=()=>I(b,_);R?R(c.el,_,A):A()}else _()},ve=(c,h)=>{let b;for(;c!==h;)b=g(c),r(c),c=b;r(h)},ge=(c,h,b)=>{const{bum:k,scope:v,job:_,subTree:I,um:R,m:A,a:S,parent:U,slots:{__:L}}=c;no(A),no(S),k&&rs(k),U&&Q(L)&&L.forEach(j=>{U.renderCache[j]=void 0}),v.stop(),_&&(_.flags|=8,de(I,c,h,b)),R&&rt(R,h),rt(()=>{c.isUnmounted=!0},h),h&&h.pendingBranch&&!h.isUnmounted&&c.asyncDep&&!c.asyncResolved&&c.suspenseId===h.pendingId&&(h.deps--,h.deps===0&&h.resolve())},nt=(c,h,b,k=!1,v=!1,_=0)=>{for(let I=_;I{if(c.shapeFlag&6)return ye(c.component.subTree);if(c.shapeFlag&128)return c.suspense.next();const h=g(c.anchor||c.el),b=h&&h[Ia];return b?g(b):h};let ht=!1;const G=(c,h,b)=>{c==null?h._vnode&&de(h._vnode,null,null,!0):x(h._vnode||null,c,h,null,null,null,b),h._vnode=c,ht||(ht=!0,Mi(),Oi(),ht=!1)},Ce={p:x,um:de,m:B,r:ae,mt:Se,mc:ee,pc:pe,pbc:se,n:ye,o:e};return{render:G,hydrate:void 0,createApp:Ha(G)}}function gr({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function rn({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Za(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function eo(e,t,n=!1){const s=e.children,r=t.children;if(Q(s)&&Q(r))for(let i=0;i>1,e[n[a]]0&&(t[s]=n[i-1]),n[i]=s)}}for(i=n.length,o=n[i-1];i-- >0;)n[i]=o,o=t[o];return n}function to(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:to(t)}function no(e){if(e)for(let t=0;tza(Qa);function on(e,t,n){return eu(e,t,n)}function eu(e,t,n=oe){const{immediate:s,deep:r,flush:i,once:o}=n,a=bt({},n),l=t&&s||!t&&i!=="post";let f;if(qn){if(i==="sync"){const y=Ja();f=y.__watcherHandles||(y.__watcherHandles=[])}else if(!l){const y=()=>{};return y.stop=Jt,y.resume=Jt,y.pause=Jt,y}}const u=Zt;a.call=(y,E,x)=>Tt(y,u,E,x);let d=!1;i==="post"?a.scheduler=y=>{rt(y,u&&u.suspense)}:i!=="sync"&&(d=!0,a.scheduler=(y,E)=>{E?y():or(y)}),a.augmentJob=y=>{t&&(y.flags|=4),d&&(y.flags|=2,u&&(y.id=u.uid,y.i=u))};const g=_a(e,t,a);return qn&&(f?f.push(g):l&&g()),g}const tu=(e,t)=>t==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${jt(t)}Modifiers`]||e[`${en(t)}Modifiers`];function nu(e,t,...n){if(e.isUnmounted)return;const s=e.vnode.props||oe;let r=n;const i=t.startsWith("update:"),o=i&&tu(s,t.slice(7));o&&(o.trim&&(r=n.map(u=>Pe(u)?u.trim():u)),o.number&&(r=n.map(Vs)));let a,l=s[a=Us(t)]||s[a=Us(jt(t))];!l&&i&&(l=s[a=Us(en(t))]),l&&Tt(l,e,6,r);const f=s[a+"Once"];if(f){if(!e.emitted)e.emitted={};else if(e.emitted[a])return;e.emitted[a]=!0,Tt(f,e,6,r)}}function su(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const i=e.emits;let o={};return i?(Q(i)?i.forEach(a=>o[a]=null):bt(o,i),Re(e)&&s.set(e,o),o):(Re(e)&&s.set(e,null),null)}function vs(e,t){return!e||!ts(t)?!1:(t=t.slice(2).replace(/Once$/,""),me(e,t[0].toLowerCase()+t.slice(1))||me(e,en(t))||me(e,t))}function Ed(){}function so(e){const{type:t,vnode:n,proxy:s,withProxy:r,propsOptions:[i],slots:o,attrs:a,emit:l,render:f,renderCache:u,props:d,data:g,setupState:y,ctx:E,inheritAttrs:x}=e,M=bs(e);let m,H;try{if(n.shapeFlag&4){const $=r||s,z=$;m=At(f.call(z,$,u,d,y,g,E)),H=a}else{const $=t;m=At($.length>1?$(d,{attrs:a,slots:o,emit:l}):$(d,null)),H=t.props?a:ru(a)}}catch($){jn.length=0,gs($,e,1),m=Oe(Ye)}let Y=m;if(H&&x!==!1){const $=Object.keys(H),{shapeFlag:z}=Y;$.length&&z&7&&(i&&$.some(Hs)&&(H=iu(H,i)),Y=Kt(Y,H,!1,!0))}return n.dirs&&(Y=Kt(Y,null,!1,!0),Y.dirs=Y.dirs?Y.dirs.concat(n.dirs):n.dirs),n.transition&&Fn(Y,n.transition),m=Y,bs(M),m}const ru=e=>{let t;for(const n in e)(n==="class"||n==="style"||ts(n))&&((t||(t={}))[n]=e[n]);return t},iu=(e,t)=>{const n={};for(const s in e)(!Hs(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function ou(e,t,n){const{props:s,children:r,component:i}=e,{props:o,children:a,patchFlag:l}=t,f=i.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&l>=0){if(l&1024)return!0;if(l&16)return s?ro(s,o,f):!!o;if(l&8){const u=t.dynamicProps;for(let d=0;de.__isSuspense;function au(e,t){t&&t.pendingBranch?Q(e)?t.effects.push(...e):t.effects.push(e):Ca(e)}const He=Symbol.for("v-fgt"),_s=Symbol.for("v-txt"),Ye=Symbol.for("v-cmt"),mr=Symbol.for("v-stc"),jn=[];let it=null;function F(e=!1){jn.push(it=e?null:[])}function uu(){jn.pop(),it=jn[jn.length-1]||null}let Vn=1;function oo(e,t=!1){Vn+=e,e<0&&it&&t&&(it.hasOnce=!0)}function lo(e){return e.dynamicChildren=Vn>0?it||mt:null,uu(),Vn>0&&it&&it.push(e),e}function W(e,t,n,s,r,i){return lo(w(e,t,n,s,r,i,!0))}function Et(e,t,n,s,r){return lo(Oe(e,t,n,s,r,!0))}function Ts(e){return e?e.__v_isVNode===!0:!1}function ln(e,t){return e.type===t.type&&e.key===t.key}const ao=({key:e})=>e!=null?e:null,ks=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?Pe(e)||Ve(e)||be(e)?{i:ct,r:e,k:t,f:!!n}:e:null);function w(e,t=null,n=null,s=0,r=null,i=e===He?0:1,o=!1,a=!1){const l={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ao(t),ref:t&&ks(t),scopeId:$i,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:ct};return a?(br(l,n),i&128&&e.normalize(l)):n&&(l.shapeFlag|=Pe(n)?8:16),Vn>0&&!o&&it&&(l.patchFlag>0||i&6)&&l.patchFlag!==32&&it.push(l),l}const Oe=cu;function cu(e,t=null,n=null,s=0,r=null,i=!1){if((!e||e===Na)&&(e=Ye),Ts(e)){const a=Kt(e,t,!0);return n&&br(a,n),Vn>0&&!i&&it&&(a.shapeFlag&6?it[it.indexOf(e)]=a:it.push(a)),a.patchFlag=-2,a}if(Tu(e)&&(e=e.__vccOpts),t){t=fu(t);let{class:a,style:l}=t;a&&!Pe(a)&&(t.class=we(a)),Re(l)&&(rr(l)&&!Q(l)&&(l=bt({},l)),t.style=qs(l))}const o=Pe(e)?1:io(e)?128:Ni(e)?64:Re(e)?4:be(e)?2:0;return w(e,t,n,s,r,o,i,!0)}function fu(e){return e?rr(e)||Ki(e)?bt({},e):e:null}function Kt(e,t,n=!1,s=!1){const{props:r,ref:i,patchFlag:o,children:a,transition:l}=e,f=t?pu(r||{},t):r,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:f,key:f&&ao(f),ref:t&&t.ref?n&&i?Q(i)?i.concat(ks(t)):[i,ks(t)]:ks(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:a,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==He?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:l,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Kt(e.ssContent),ssFallback:e.ssFallback&&Kt(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return l&&s&&Fn(u,l.clone(u)),u}function Ss(e=" ",t=0){return Oe(_s,null,e,t)}function qe(e="",t=!1){return t?(F(),Et(Ye,null,e)):Oe(Ye,null,e)}function At(e){return e==null||typeof e=="boolean"?Oe(Ye):Q(e)?Oe(He,null,e.slice()):Ts(e)?Yt(e):Oe(_s,null,String(e))}function Yt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Kt(e)}function br(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(Q(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),br(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!Ki(t)?t._ctx=ct:r===3&&ct&&(ct.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else be(t)?(t={default:t,_ctx:ct},n=32):(t=String(t),s&64?(n=16,t=[Ss(t)]):n=8);e.children=t,e.shapeFlag|=n}function pu(...e){const t={};for(let n=0;nZt||ct;let Es,xr;{const e=is(),t=(n,s)=>{let r;return(r=e[n])||(r=e[n]=[]),r.push(s),i=>{r.length>1?r.forEach(o=>o(i)):r[0](i)}};Es=t("__VUE_INSTANCE_SETTERS__",n=>Zt=n),xr=t("__VUE_SSR_SETTERS__",n=>qn=n)}const yr=e=>{const t=Zt;return Es(e),e.scope.on(),()=>{e.scope.off(),Es(t)}},co=()=>{Zt&&Zt.scope.off(),Es(null)};function fo(e){return e.vnode.shapeFlag&4}let qn=!1;function mu(e,t=!1,n=!1){t&&xr(t);const{props:s,children:r}=e.vnode,i=fo(e);Ua(e,s,i,t),Wa(e,r,n||t);const o=i?bu(e,t):void 0;return t&&xr(!1),o}function bu(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,Fa);const{setup:s}=n;if(s){It();const r=e.setupContext=s.length>1?yu(e):null,i=yr(e),o=bn(s,e,0,[e.props,r]),a=ti(o);if(Mt(),i(),(a||e.sp)&&!Hn(e)&&$a(e),a){if(o.then(co,co),t)return o.then(l=>{po(e,l)}).catch(l=>{gs(l,e,0)});e.asyncDep=o}else po(e,o)}else ho(e)}function po(e,t,n){be(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Re(t)&&(e.setupState=Ri(t)),ho(e)}function ho(e,t,n){const s=e.type;e.render||(e.render=s.render||Jt)}const xu={get(e,t){return je(e,"get",""),e[t]}};function yu(e){const t=n=>{e.exposed=n||{}};return{attrs:new Proxy(e.attrs,xu),slots:e.slots,emit:e.emit,expose:t}}function As(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(Ri(ha(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Un)return Un[n](e)},has(t,n){return n in t||n in Un}})):e.proxy}const wu=/(?:^|[-_])(\w)/g,vu=e=>e.replace(wu,t=>t.toUpperCase()).replace(/[-_]/g,"");function _u(e,t=!0){return be(e)?e.displayName||e.name:e.name||t&&e.__name}function go(e,t,n=!1){let s=_u(t);if(!s&&t.__file){const r=t.__file.match(/([^/\\]+)\.\w+$/);r&&(s=r[1])}if(!s&&e&&e.parent){const r=i=>{for(const o in i)if(i[o]===t)return o};s=r(e.components||e.parent.type.components)||r(e.appContext.components)}return s?vu(s):n?"App":"Anonymous"}function Tu(e){return be(e)&&"__vccOpts"in e}const Ee=(e,t)=>wa(e,t,qn);function ku(e,t,n){const s=arguments.length;return s===2?Re(t)&&!Q(t)?Ts(t)?Oe(e,null,[t]):Oe(e,t):Oe(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&Ts(n)&&(n=[n]),Oe(e,t,n))}const Su="3.5.18";let wr;const mo=typeof window!="undefined"&&window.trustedTypes;if(mo)try{wr=mo.createPolicy("vue",{createHTML:e=>e})}catch{}const bo=wr?e=>wr.createHTML(e):e=>e,Eu="http://www.w3.org/2000/svg",Au="http://www.w3.org/1998/Math/MathML",Dt=typeof document!="undefined"?document:null,xo=Dt&&Dt.createElement("template"),Ru={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t==="svg"?Dt.createElementNS(Eu,e):t==="mathml"?Dt.createElementNS(Au,e):n?Dt.createElement(e,{is:n}):Dt.createElement(e);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>Dt.createTextNode(e),createComment:e=>Dt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Dt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,s,r,i){const o=n?n.previousSibling:t.lastChild;if(r&&(r===i||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===i||!(r=r.nextSibling)););else{xo.innerHTML=bo(s==="svg"?`${e}`:s==="mathml"?`${e}`:e);const a=xo.content;if(s==="svg"||s==="mathml"){const l=a.firstChild;for(;l.firstChild;)a.appendChild(l.firstChild);a.removeChild(l)}t.insertBefore(a,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Xt="transition",Wn="animation",Gn=Symbol("_vtc"),yo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},Cu=bt({},Fi,yo),Pu=(e=>(e.displayName="Transition",e.props=Cu,e))((e,{slots:t})=>ku(La,Iu(e),t)),an=(e,t=[])=>{Q(e)?e.forEach(n=>n(...t)):e&&e(...t)},wo=e=>e?Q(e)?e.some(t=>t.length>1):e.length>1:!1;function Iu(e){const t={};for(const D in e)D in yo||(t[D]=e[D]);if(e.css===!1)return t;const{name:n="v",type:s,duration:r,enterFromClass:i=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:a=`${n}-enter-to`,appearFromClass:l=i,appearActiveClass:f=o,appearToClass:u=a,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:g=`${n}-leave-active`,leaveToClass:y=`${n}-leave-to`}=e,E=Mu(r),x=E&&E[0],M=E&&E[1],{onBeforeEnter:m,onEnter:H,onEnterCancelled:Y,onLeave:$,onLeaveCancelled:z,onBeforeAppear:O=m,onAppear:P=H,onAppearCancelled:ee=Y}=t,V=(D,Z,Se,Ue)=>{D._enterCancelled=Ue,un(D,Z?u:a),un(D,Z?f:o),Se&&Se()},se=(D,Z)=>{D._isLeaving=!1,un(D,d),un(D,y),un(D,g),Z&&Z()},fe=D=>(Z,Se)=>{const Ue=D?P:H,te=()=>V(Z,D,Se);an(Ue,[Z,te]),vo(()=>{un(Z,D?l:i),Nt(Z,D?u:a),wo(Ue)||_o(Z,s,x,te)})};return bt(t,{onBeforeEnter(D){an(m,[D]),Nt(D,i),Nt(D,o)},onBeforeAppear(D){an(O,[D]),Nt(D,l),Nt(D,f)},onEnter:fe(!1),onAppear:fe(!0),onLeave(D,Z){D._isLeaving=!0;const Se=()=>se(D,Z);Nt(D,d),D._enterCancelled?(Nt(D,g),So()):(So(),Nt(D,g)),vo(()=>{D._isLeaving&&(un(D,d),Nt(D,y),wo($)||_o(D,s,M,Se))}),an($,[D,Se])},onEnterCancelled(D){V(D,!1,void 0,!0),an(Y,[D])},onAppearCancelled(D){V(D,!0,void 0,!0),an(ee,[D])},onLeaveCancelled(D){se(D),an(z,[D])}})}function Mu(e){if(e==null)return null;if(Re(e))return[vr(e.enter),vr(e.leave)];{const t=vr(e);return[t,t]}}function vr(e){return zl(e)}function Nt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[Gn]||(e[Gn]=new Set)).add(t)}function un(e,t){t.split(/\s+/).forEach(s=>s&&e.classList.remove(s));const n=e[Gn];n&&(n.delete(t),n.size||(e[Gn]=void 0))}function vo(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Ou=0;function _o(e,t,n,s){const r=e._endId=++Ou,i=()=>{r===e._endId&&s()};if(n!=null)return setTimeout(i,n);const{type:o,timeout:a,propCount:l}=Lu(e,t);if(!o)return s();const f=o+"end";let u=0;const d=()=>{e.removeEventListener(f,g),i()},g=y=>{y.target===e&&++u>=l&&d()};setTimeout(()=>{u(n[E]||"").split(", "),r=s(`${Xt}Delay`),i=s(`${Xt}Duration`),o=To(r,i),a=s(`${Wn}Delay`),l=s(`${Wn}Duration`),f=To(a,l);let u=null,d=0,g=0;t===Xt?o>0&&(u=Xt,d=o,g=i.length):t===Wn?f>0&&(u=Wn,d=f,g=l.length):(d=Math.max(o,f),u=d>0?o>f?Xt:Wn:null,g=u?u===Xt?i.length:l.length:0);const y=u===Xt&&/\b(transform|all)(,|$)/.test(s(`${Xt}Property`).toString());return{type:u,timeout:d,propCount:g,hasTransform:y}}function To(e,t){for(;e.lengthko(n)+ko(e[s])))}function ko(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function So(){return document.body.offsetHeight}function $u(e,t,n){const s=e[Gn];s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Eo=Symbol("_vod"),Du=Symbol("_vsh"),Nu=Symbol(""),Fu=/(^|;)\s*display\s*:/;function Bu(e,t,n){const s=e.style,r=Pe(n);let i=!1;if(n&&!r){if(t)if(Pe(t))for(const o of t.split(";")){const a=o.slice(0,o.indexOf(":")).trim();n[a]==null&&Rs(s,a,"")}else for(const o in t)n[o]==null&&Rs(s,o,"");for(const o in n)o==="display"&&(i=!0),Rs(s,o,n[o])}else if(r){if(t!==n){const o=s[Nu];o&&(n+=";"+o),s.cssText=n,i=Fu.test(n)}}else t&&e.removeAttribute("style");Eo in e&&(e[Eo]=i?s.display:"",e[Du]&&(s.display="none"))}const Ao=/\s*!important$/;function Rs(e,t,n){if(Q(n))n.forEach(s=>Rs(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=Hu(e,t);Ao.test(n)?e.setProperty(en(s),n.replace(Ao,""),"important"):e[s]=n}}const Ro=["Webkit","Moz","ms"],_r={};function Hu(e,t){const n=_r[t];if(n)return n;let s=jt(t);if(s!=="filter"&&s in e)return _r[t]=s;s=ri(s);for(let r=0;rTr||(Vu.then(()=>Tr=0),Tr=Date.now());function Wu(e,t){const n=s=>{if(!s._vts)s._vts=Date.now();else if(s._vts<=n.attached)return;Tt(Gu(s,n.value),t,5,[s])};return n.value=e,n.attached=qu(),n}function Gu(e,t){if(Q(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const Lo=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Ku=(e,t,n,s,r,i)=>{const o=r==="svg";t==="class"?$u(e,s,o):t==="style"?Bu(e,n,s):ts(t)?Hs(t)||Uu(e,t,n,s,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Yu(e,t,s,o))?(Io(e,t,s),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&Po(e,t,s,o,i,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!Pe(s))?Io(e,jt(t),s,i,t):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),Po(e,t,s,o))};function Yu(e,t,n,s){if(s)return!!(t==="innerHTML"||t==="textContent"||t in e&&Lo(t)&&be(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const r=e.tagName;if(r==="IMG"||r==="VIDEO"||r==="CANVAS"||r==="SOURCE")return!1}return Lo(t)&&Pe(n)?!1:t in e}const $o=e=>{const t=e.props["onUpdate:modelValue"]||!1;return Q(t)?n=>rs(t,n):t};function Zu(e){e.target.composing=!0}function Do(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const kr=Symbol("_assign"),Xu={created(e,{modifiers:{lazy:t,trim:n,number:s}},r){e[kr]=$o(r);const i=s||r.props&&r.props.type==="number";vn(e,t?"change":"input",o=>{if(o.target.composing)return;let a=e.value;n&&(a=a.trim()),i&&(a=Vs(a)),e[kr](a)}),n&&vn(e,"change",()=>{e.value=e.value.trim()}),t||(vn(e,"compositionstart",Zu),vn(e,"compositionend",Do),vn(e,"change",Do))},mounted(e,{value:t}){e.value=t==null?"":t},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:s,trim:r,number:i}},o){if(e[kr]=$o(o),e.composing)return;const a=(i||e.type==="number")&&!/^0\d/.test(e.value)?Vs(e.value):e.value,l=t==null?"":t;a!==l&&(document.activeElement===e&&e.type!=="range"&&(s&&t===n||r&&e.value.trim()===l)||(e.value=l))}},Qu=["ctrl","shift","alt","meta"],Ju={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Qu.some(n=>e[`${n}Key`]&&!t.includes(n))},pt=(e,t)=>{const n=e._withMods||(e._withMods={}),s=t.join(".");return n[s]||(n[s]=(r,...i)=>{for(let o=0;o{const t=tc().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=rc(s);if(!r)return;const i=t._component;!be(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent="");const o=n(r,!1,sc(r));return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),o},t};function sc(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function rc(e){return Pe(e)?document.querySelector(e):e}const ic=["aria-pressed"],oc={key:0,viewBox:"0 0 24 24",width:"20",height:"20","aria-hidden":"true"},lc={key:1,viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",fill:"none"},ac={__name:"ChatbotToggler",props:{isOpen:{type:Boolean,required:!0}},emits:["toggle"],setup(e){return(t,n)=>(F(),W("button",{class:"fixed bottom-5 right-5 z-9999 grid h-12 w-12 appearance-none place-items-center rounded-full border border-white/20 bg-gradient-to-br from-brand-500 to-brand-600 text-white shadow-[0_20px_36px_-20px_rgba(109,79,194,0.85)] transition-all duration-250 hover:-translate-y-0.5 hover:from-brand-600 hover:to-violet-700 hover:shadow-[0_22px_40px_-22px_rgba(109,79,194,1)] focus:outline-none max-[600px]:bottom-3 max-[600px]:right-3 max-[600px]:h-13 max-[600px]:w-13",style:{"border-radius":"9999px"},"aria-pressed":e.isOpen?"true":"false",onClick:n[0]||(n[0]=s=>t.$emit("toggle"))},[e.isOpen?(F(),W("svg",lc,n[2]||(n[2]=[w("path",{d:"M6 6l12 12M18 6L6 18",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round"},null,-1)]))):(F(),W("svg",oc,n[1]||(n[1]=[w("path",{d:"M4 4h16a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H8l-4 4v-4H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z",fill:"currentColor",stroke:"currentColor","stroke-width":"2","stroke-linecap":"miter","stroke-linejoin":"miter"},null,-1)])))],8,ic))}},uc={class:"chat-header relative flex min-h-14 items-center justify-between px-4 pb-2.5 pt-3 text-white sm:px-5"},cc={class:"flex min-w-0 flex-1 items-center gap-2 sm:gap-2.5"},fc={xmlns:"http://www.w3.org/2000/svg",width:"35",height:"35",viewBox:"0 0 1024 1024",class:"h-8 w-8 shrink-0 rounded-full bg-white p-1.5 shadow-md motion-safe:animate-soft-float",style:{fill:"#6d4fc2"}},pc={class:"ml-2 flex items-center gap-1.5"},dc=["title"],hc=["title","aria-label"],gc={key:0,viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},mc={key:1,viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},bc=["title","aria-label"],xc={key:0,xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},yc={key:1,xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},wc={key:2,xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},vc={__name:"ChatHeader",props:{windowMode:{type:String,required:!0},autoReadEnabled:{type:Boolean,required:!0},activeTtsProvider:{type:String,required:!0}},emits:["close","cycleResize","toggleAutoRead"],setup(e){const t=e,n={default:"Compact",half:"Half Screen",full:"Full Screen"},s=Ee(()=>n[t.windowMode]||"Compact"),r=Ee(()=>t.windowMode==="default"?"Half Screen":t.windowMode==="half"?"Full Screen":"Compact"),i=Ee(()=>t.activeTtsProvider==="polly"?"TTS: Polly":t.activeTtsProvider==="browser"?"TTS: Browser":"TTS: Off"),o=Ee(()=>t.activeTtsProvider==="polly"?"bg-emerald-500/45":t.activeTtsProvider==="browser"?"bg-amber-500/45":"bg-slate-500/35");return(a,l)=>(F(),W("div",uc,[w("div",cc,[(F(),W("svg",fc,l[3]||(l[3]=[w("path",{d:"M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z"},null,-1)]))),l[4]||(l[4]=w("h2",{class:"truncate text-xs font-semibold tracking-[0.01em] sm:text-base text-white/95"},"ChangAI from ERPGulf",-1))]),w("div",pc,[w("span",{class:we(["hidden rounded-full border border-white/25 px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-white/95 shadow-sm backdrop-blur-sm sm:inline",o.value]),title:`TTS provider: ${i.value}`},Je(i.value),11,dc),w("button",{class:we(["h-8 min-w-8 appearance-none items-center justify-center rounded-md border border-white/20 px-2 text-xs font-semibold text-white/90 transition-all duration-200 focus:outline-none sm:flex",e.autoReadEnabled?"bg-white/24 shadow-sm":"hover:bg-white/15"]),style:{"border-radius":"0.375rem"},title:e.autoReadEnabled?"Auto speech on":"Auto speech off","aria-label":e.autoReadEnabled?"Turn off auto speech":"Turn on auto speech",onClick:l[0]||(l[0]=f=>a.$emit("toggleAutoRead"))},[e.autoReadEnabled?(F(),W("svg",gc,l[5]||(l[5]=[w("path",{d:"M11 5L6 9H3v6h3l5 4V5z"},null,-1),w("path",{d:"M15 9a4 4 0 0 1 0 6"},null,-1),w("path",{d:"M18 7a7 7 0 0 1 0 10"},null,-1)]))):(F(),W("svg",mc,l[6]||(l[6]=[w("path",{d:"M11 5L6 9H3v6h3l5 4V5z"},null,-1),w("path",{d:"M22 9l-6 6"},null,-1),w("path",{d:"M16 9l6 6"},null,-1)])))],10,hc),w("button",{class:we(["flex h-8 min-w-8 appearance-none items-center justify-center rounded-md border border-white/20 px-2 text-xs font-semibold text-white/90 transition-all duration-200 focus:outline-none","bg-white/20 shadow-sm hover:bg-white/25"]),style:{"border-radius":"0.375rem"},title:`Resize mode: ${s.value} (click to ${r.value})`,"aria-label":`Resize mode ${s.value}. Click to switch to ${r.value}`,onClick:l[1]||(l[1]=f=>a.$emit("cycleResize"))},[e.windowMode==="default"?(F(),W("svg",xc,l[7]||(l[7]=[w("rect",{x:"7",y:"8",width:"10",height:"8",rx:"2"},null,-1)]))):e.windowMode==="half"?(F(),W("svg",yc,l[8]||(l[8]=[w("rect",{x:"4",y:"5",width:"16",height:"14",rx:"2"},null,-1),w("path",{d:"M12 5v14"},null,-1)]))):(F(),W("svg",wc,l[9]||(l[9]=[w("rect",{x:"4",y:"5",width:"16",height:"14",rx:"2"},null,-1),w("path",{d:"M8 8H6v2M16 8h2v2M8 16H6v-2M16 16h2v-2"},null,-1)])))],8,bc),w("button",{class:"grid h-8 w-8 shrink-0 appearance-none place-items-center rounded-full border border-white/20 text-white transition-all duration-200 hover:scale-105 hover:bg-white/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/70",style:{"border-radius":"9999px"},"aria-label":"Close chatbot",onClick:l[2]||(l[2]=f=>a.$emit("close"))},l[10]||(l[10]=[w("svg",{xmlns:"http://www.w3.org/2000/svg",height:"24",width:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[w("path",{d:"M6 9l6 6 6-6"})],-1)]))])]))}},_c={class:"flex gap-1.5 border-b border-slate-200/80 px-2.5 pb-2.5 pt-1"},Tc=["onClick"],kc={class:"inline-flex items-center gap-1.5"},Sc={__name:"TabBar",props:{modelValue:{type:String,required:!0},debugEnabled:{type:Boolean,default:!1}},emits:["update:modelValue"],setup(e){const t=e,n=Ee(()=>{const s=[{id:"chat",label:"Chats"},{id:"debug",label:"Debug"},{id:"support",label:"Support"},{id:"settings",label:"Settings"}];return t.debugEnabled?s:s.filter(r=>r.id!=="debug")});return(s,r)=>(F(),W("div",_c,[(F(!0),W(He,null,ws(n.value,i=>(F(),W("button",{key:i.id,class:we(["group min-w-0 flex-1 h-9 appearance-none rounded-lg border border-transparent bg-transparent px-2 text-xs font-semibold transition-all duration-200 focus:outline-none",e.modelValue===i.id?"border-white/30 bg-linear-to-r from-violet-300/36 via-indigo-300/30 to-sky-300/28 text-white shadow-[0_4px_10px_rgba(20,24,40,0.22)]":"text-white/80 hover:border-white/25 hover:bg-white/12 hover:text-white"]),onClick:o=>s.$emit("update:modelValue",i.id)},[w("span",kc,[w("span",{class:we(["h-1.5 w-1.5 rounded-full transition-colors duration-200",e.modelValue===i.id?"bg-white":"bg-white/40 group-hover:bg-white/70"])},null,2),Ss(" "+Je(i.label),1)])],10,Tc))),128))]))}},Ec=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Ac={},Rc={xmlns:"http://www.w3.org/2000/svg",width:"50",height:"50",viewBox:"0 0 1024 1024",class:"h-7.5 w-7.5 shrink-0 self-end rounded-full bg-gradient-to-br from-brand-500 to-brand-600 p-1.5 fill-white shadow-[0_10px_18px_-12px_rgba(109,79,194,0.85)]"};function Cc(e,t){return F(),W("svg",Rc,t[0]||(t[0]=[w("path",{d:"M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z"},null,-1)]))}const Fo=Ec(Ac,[["render",Cc]]),Cs={PIPELINE:"changai.changai.api.v2.text2sql_pipeline_v2.run_text2sql_pipeline",SUPPORT:"changai.changai.api.v2.text2sql_pipeline_v2.support_bot",SETTINGS:"changai.changai.api.v2.text2sql_pipeline_v2.get_frontend_settings",TTS:"changai.changai.api.v2.text2sql_pipeline_v2.synthesize_tts"};function Bo(e,t={},n="actual"){return n==="test"?Promise.resolve({Bot:`[TEST MODE] ${JSON.stringify(t)}`}):!window.frappe||!window.frappe.call?Promise.reject(new Error("Frappe API is unavailable in actual mode.")):new Promise((s,r)=>{window.frappe.call({method:e,args:t,callback(i){s(i.message)},error(i){r(i)}})})}function Pc(e,t,n="actual",s=null,r){if(n==="test")return{promise:Promise.resolve({Bot:`[TEST MODE] ${JSON.stringify({user_question:e,chat_id:t,request_id:s,sendNonErptoAI:r})}`}),cancel:()=>!1};if(!window.frappe||!window.frappe.call)return{promise:Promise.reject(new Error("Frappe API is unavailable in actual mode.")),cancel:()=>!1};let i=null,o=!1;return{promise:new Promise((f,u)=>{i=window.frappe.call({method:Cs.PIPELINE,args:{user_question:e,chat_id:t,request_id:s,sendNonErptoAI:r},callback(d){o=!0,f(d.message)},error(d){o=!0,u(d)}})}),cancel:()=>o||!i||typeof i.abort!="function"?!1:(i.abort(),o=!0,!0)}}function Ic(e,t="actual"){if(t==="test")return{promise:Promise.resolve(`[TEST MODE] ${JSON.stringify({message:e})}`),cancel:()=>!1};if(!window.frappe||!window.frappe.call)return{promise:Promise.reject(new Error("Frappe API is unavailable in actual mode.")),cancel:()=>!1};let n=null,s=!1;return{promise:new Promise((o,a)=>{n=window.frappe.call({method:Cs.SUPPORT,args:{message:e},callback(l){s=!0,o(l.message)},error(l){s=!0,a(l)}})}),cancel:()=>s||!n||typeof n.abort!="function"?!1:(n.abort(),s=!0,!0)}}function Mc(e="actual"){return Bo(Cs.SETTINGS,{},e)}function Oc(e,t="Zayd",n="actual"){return Bo(Cs.TTS,{text:e,voice_id:t},n)}function Sr(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var cn=Sr();function Ho(e){cn=e}var fn={exec:()=>null};function le(e,t=""){let n=typeof e=="string"?e:e.source,s={replace:(r,i)=>{let o=typeof i=="string"?i:i.source;return o=o.replace(We.caret,"$1"),n=n.replace(r,o),s},getRegex:()=>new RegExp(n,t)};return s}var Lc=((e="")=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i"),blockquoteBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}>`)},$c=/^(?:[ \t]*(?:\n|$))+/,Dc=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Nc=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,Kn=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Fc=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,Er=/ {0,3}(?:[*+-]|\d{1,9}[.)])/,zo=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,Uo=le(zo).replace(/bull/g,Er).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Bc=le(zo).replace(/bull/g,Er).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),Ar=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,Hc=/^[^\n]+/,Rr=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,zc=le(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",Rr).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),Uc=le(/^(bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,Er).getRegex(),Ps="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Cr=/|$))/,jc=le("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",Cr).replace("tag",Ps).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),jo=le(Ar).replace("hr",Kn).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ps).getRegex(),Vc=le(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",jo).getRegex(),Pr={blockquote:Vc,code:Dc,def:zc,fences:Nc,heading:Fc,hr:Kn,html:jc,lheading:Uo,list:Uc,newline:$c,paragraph:jo,table:fn,text:Hc},Vo=le("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",Kn).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ps).getRegex(),qc={...Pr,lheading:Bc,table:Vo,paragraph:le(Ar).replace("hr",Kn).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",Vo).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ps).getRegex()},Wc={...Pr,html:le(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",Cr).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:fn,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:le(Ar).replace("hr",Kn).replace("heading",` *#{1,6} *[^ -]`).replace("lheading",Uo).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Gc=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,Kc=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,qo=/^( {2,}|\\)\n(?!\s*$)/,Yc=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Lc?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),Go=/^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/,ef=le(Go,"u").replace(/punct/g,_n).getRegex(),tf=le(Go,"u").replace(/punct/g,Wo).getRegex(),Ko="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",nf=le(Ko,"gu").replace(/notPunctSpace/g,Ir).replace(/punctSpace/g,Is).replace(/punct/g,_n).getRegex(),sf=le(Ko,"gu").replace(/notPunctSpace/g,Qc).replace(/punctSpace/g,Xc).replace(/punct/g,Wo).getRegex(),rf=le("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,Ir).replace(/punctSpace/g,Is).replace(/punct/g,_n).getRegex(),of=le(/^~~?(?:((?!~)punct)|[^\s~])/,"u").replace(/punct/g,_n).getRegex(),lf="^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)",af=le(lf,"gu").replace(/notPunctSpace/g,Ir).replace(/punctSpace/g,Is).replace(/punct/g,_n).getRegex(),uf=le(/\\(punct)/,"gu").replace(/punct/g,_n).getRegex(),cf=le(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),ff=le(Cr).replace("(?:-->|$)","-->").getRegex(),pf=le("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",ff).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),Ms=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/,df=le(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label",Ms).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),Yo=le(/^!?\[(label)\]\[(ref)\]/).replace("label",Ms).replace("ref",Rr).getRegex(),Zo=le(/^!?\[(ref)\](?:\[\])?/).replace("ref",Rr).getRegex(),hf=le("reflink|nolink(?!\\()","g").replace("reflink",Yo).replace("nolink",Zo).getRegex(),Xo=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,Mr={_backpedal:fn,anyPunctuation:uf,autolink:cf,blockSkip:Jc,br:qo,code:Kc,del:fn,delLDelim:fn,delRDelim:fn,emStrongLDelim:ef,emStrongRDelimAst:nf,emStrongRDelimUnd:rf,escape:Gc,link:df,nolink:Zo,punctuation:Zc,reflink:Yo,reflinkSearch:hf,tag:pf,text:Yc,url:fn},gf={...Mr,link:le(/^!?\[(label)\]\((.*?)\)/).replace("label",Ms).getRegex(),reflink:le(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ms).getRegex()},Or={...Mr,emStrongRDelimAst:sf,emStrongLDelim:tf,delLDelim:of,delRDelim:af,url:le(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",Xo).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:le(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},Qo=e=>bf[e];function Ct(e,t){if(t){if(We.escapeTest.test(e))return e.replace(We.escapeReplace,Qo)}else if(We.escapeTestNoEncode.test(e))return e.replace(We.escapeReplaceNoEncode,Qo);return e}function Jo(e){try{e=encodeURI(e).replace(We.percentDecode,"%")}catch{return null}return e}function el(e,t){var i;let n=e.replace(We.findPipe,(o,a,l)=>{let f=!1,u=a;for(;--u>=0&&l[u]==="\\";)f=!f;return f?"|":" |"}),s=n.split(We.splitPipe),r=0;if(s[0].trim()||s.shift(),s.length>0&&!((i=s.at(-1))!=null&&i.trim())&&s.pop(),t)if(s.length>t)s.splice(t);else for(;s.length=0&&We.blankLine.test(t[n]);)n--;return t.length-n<=2?e:t.slice(0,n+1).join(` -`)}function xf(e,t){if(e.indexOf(t[1])===-1)return-1;let n=0;for(let s=0;s0?-2:-1}function yf(e,t=0){let n=t,s="";for(let r of e)if(r===" "){let i=4-n%4;s+=" ".repeat(i),n+=i}else s+=r,n++;return s}function nl(e,t,n,s,r){let i=t.href,o=t.title||null,a=e[1].replace(r.other.outputLinkReplace,"$1");s.state.inLink=!0;let l={type:e[0].charAt(0)==="!"?"image":"link",raw:n,href:i,title:o,text:a,tokens:s.inlineTokens(a)};return s.state.inLink=!1,l}function wf(e,t,n){let s=e.match(n.other.indentCodeCompensation);if(s===null)return t;let r=s[1];return t.split(` -`).map(i=>{let o=i.match(n.other.beginningSpace);if(o===null)return i;let[a]=o;return a.length>=r.length?i.slice(r.length):i}).join(` -`)}var Ls=class{constructor(e){_e(this,"options");_e(this,"rules");_e(this,"lexer");this.options=e||cn}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=this.options.pedantic?t[0]:tl(t[0]),s=n.replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:n,codeBlockStyle:"indented",text:s}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=wf(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=Qt(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:Qt(t[0],` -`),depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:Qt(t[0],` -`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=Qt(t[0],` +var vd = Object.defineProperty; var _d = (Pt, oe, mt) => oe in Pt ? vd(Pt, oe, { enumerable: !0, configurable: !0, writable: !0, value: mt }) : Pt[oe] = mt; var _e = (Pt, oe, mt) => _d(Pt, typeof oe != "symbol" ? oe + "" : oe, mt); (function () { + "use strict"; var Fs; function Pt(e) { const t = Object.create(null); for (const n of e.split(",")) t[n] = 1; return n => n in t } const oe = {}, mt = [], Jt = () => { }, Dl = () => !1, ts = e => e.charCodeAt(0) === 111 && e.charCodeAt(1) === 110 && (e.charCodeAt(2) > 122 || e.charCodeAt(2) < 97), Hs = e => e.startsWith("onUpdate:"), bt = Object.assign, Jr = (e, t) => { const n = e.indexOf(t); n > -1 && e.splice(n, 1) }, Nl = Object.prototype.hasOwnProperty, me = (e, t) => Nl.call(e, t), Q = Array.isArray, hn = e => ns(e) === "[object Map]", ei = e => ns(e) === "[object Set]", be = e => typeof e == "function", Pe = e => typeof e == "string", Ut = e => typeof e == "symbol", Re = e => e !== null && typeof e == "object", ti = e => (Re(e) || be(e)) && be(e.then) && be(e.catch), ni = Object.prototype.toString, ns = e => ni.call(e), Fl = e => ns(e).slice(8, -1), si = e => ns(e) === "[object Object]", zs = e => Pe(e) && e !== "NaN" && e[0] !== "-" && "" + parseInt(e, 10) === e, Pn = Pt(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"), ss = e => { const t = Object.create(null); return n => t[n] || (t[n] = e(n)) }, Bl = /-(\w)/g, jt = ss(e => e.replace(Bl, (t, n) => n ? n.toUpperCase() : "")), Hl = /\B([A-Z])/g, en = ss(e => e.replace(Hl, "-$1").toLowerCase()), ri = ss(e => e.charAt(0).toUpperCase() + e.slice(1)), Us = ss(e => e ? `on${ri(e)}` : ""), Vt = (e, t) => !Object.is(e, t), rs = (e, ...t) => { for (let n = 0; n < e.length; n++)e[n](...t) }, js = (e, t, n, s = !1) => { Object.defineProperty(e, t, { configurable: !0, enumerable: !1, writable: s, value: n }) }, Vs = e => { const t = parseFloat(e); return isNaN(t) ? e : t }, zl = e => { const t = Pe(e) ? Number(e) : NaN; return isNaN(t) ? e : t }; let ii; const is = () => ii || (ii = typeof globalThis != "undefined" ? globalThis : typeof self != "undefined" ? self : typeof window != "undefined" ? window : typeof global != "undefined" ? global : {}); function qs(e) { if (Q(e)) { const t = {}; for (let n = 0; n < e.length; n++) { const s = e[n], r = Pe(s) ? ql(s) : qs(s); if (r) for (const i in r) t[i] = r[i] } return t } else if (Pe(e) || Re(e)) return e } const Ul = /;(?![^(]*\))/g, jl = /:([^]+)/, Vl = /\/\*[^]*?\*\//g; function ql(e) { const t = {}; return e.replace(Vl, "").split(Ul).forEach(n => { if (n) { const s = n.split(jl); s.length > 1 && (t[s[0].trim()] = s[1].trim()) } }), t } function we(e) { let t = ""; if (Pe(e)) t = e; else if (Q(e)) for (let n = 0; n < e.length; n++) { const s = we(e[n]); s && (t += s + " ") } else if (Re(e)) for (const n in e) e[n] && (t += n + " "); return t.trim() } const Wl = Pt("itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly"); function oi(e) { return !!e || e === "" } const li = e => !!(e && e.__v_isRef === !0), Je = e => Pe(e) ? e : e == null ? "" : Q(e) || Re(e) && (e.toString === ni || !be(e.toString)) ? li(e) ? Je(e.value) : JSON.stringify(e, ai, 2) : String(e), ai = (e, t) => li(t) ? ai(e, t.value) : hn(t) ? { [`Map(${t.size})`]: [...t.entries()].reduce((n, [s, r], i) => (n[Ws(s, i) + " =>"] = r, n), {}) } : ei(t) ? { [`Set(${t.size})`]: [...t.values()].map(n => Ws(n)) } : Ut(t) ? Ws(t) : Re(t) && !Q(t) && !si(t) ? String(t) : t, Ws = (e, t = "") => { var n; return Ut(e) ? `Symbol(${(n = e.description) != null ? n : t})` : e }; let et; class Gl { constructor(t = !1) { this.detached = t, this._active = !0, this._on = 0, this.effects = [], this.cleanups = [], this._isPaused = !1, this.parent = et, !t && et && (this.index = (et.scopes || (et.scopes = [])).push(this) - 1) } get active() { return this._active } pause() { if (this._active) { this._isPaused = !0; let t, n; if (this.scopes) for (t = 0, n = this.scopes.length; t < n; t++)this.scopes[t].pause(); for (t = 0, n = this.effects.length; t < n; t++)this.effects[t].pause() } } resume() { if (this._active && this._isPaused) { this._isPaused = !1; let t, n; if (this.scopes) for (t = 0, n = this.scopes.length; t < n; t++)this.scopes[t].resume(); for (t = 0, n = this.effects.length; t < n; t++)this.effects[t].resume() } } run(t) { if (this._active) { const n = et; try { return et = this, t() } finally { et = n } } } on() { ++this._on === 1 && (this.prevScope = et, et = this) } off() { this._on > 0 && --this._on === 0 && (et = this.prevScope, this.prevScope = void 0) } stop(t) { if (this._active) { this._active = !1; let n, s; for (n = 0, s = this.effects.length; n < s; n++)this.effects[n].stop(); for (this.effects.length = 0, n = 0, s = this.cleanups.length; n < s; n++)this.cleanups[n](); if (this.cleanups.length = 0, this.scopes) { for (n = 0, s = this.scopes.length; n < s; n++)this.scopes[n].stop(!0); this.scopes.length = 0 } if (!this.detached && this.parent && !t) { const r = this.parent.scopes.pop(); r && r !== this && (this.parent.scopes[this.index] = r, r.index = this.index) } this.parent = void 0 } } } function Kl() { return et } let Te; const Gs = new WeakSet; class ui { constructor(t) { this.fn = t, this.deps = void 0, this.depsTail = void 0, this.flags = 5, this.next = void 0, this.cleanup = void 0, this.scheduler = void 0, et && et.active && et.effects.push(this) } pause() { this.flags |= 64 } resume() { this.flags & 64 && (this.flags &= -65, Gs.has(this) && (Gs.delete(this), this.trigger())) } notify() { this.flags & 2 && !(this.flags & 32) || this.flags & 8 || fi(this) } run() { if (!(this.flags & 1)) return this.fn(); this.flags |= 2, mi(this), pi(this); const t = Te, n = xt; Te = this, xt = !0; try { return this.fn() } finally { di(this), Te = t, xt = n, this.flags &= -3 } } stop() { if (this.flags & 1) { for (let t = this.deps; t; t = t.nextDep)Xs(t); this.deps = this.depsTail = void 0, mi(this), this.onStop && this.onStop(), this.flags &= -2 } } trigger() { this.flags & 64 ? Gs.add(this) : this.scheduler ? this.scheduler() : this.runIfDirty() } runIfDirty() { Zs(this) && this.run() } get dirty() { return Zs(this) } } let ci = 0, In, Mn; function fi(e, t = !1) { if (e.flags |= 8, t) { e.next = Mn, Mn = e; return } e.next = In, In = e } function Ks() { ci++ } function Ys() { if (--ci > 0) return; if (Mn) { let t = Mn; for (Mn = void 0; t;) { const n = t.next; t.next = void 0, t.flags &= -9, t = n } } let e; for (; In;) { let t = In; for (In = void 0; t;) { const n = t.next; if (t.next = void 0, t.flags &= -9, t.flags & 1) try { t.trigger() } catch (s) { e || (e = s) } t = n } } if (e) throw e } function pi(e) { for (let t = e.deps; t; t = t.nextDep)t.version = -1, t.prevActiveLink = t.dep.activeLink, t.dep.activeLink = t } function di(e) { let t, n = e.depsTail, s = n; for (; s;) { const r = s.prevDep; s.version === -1 ? (s === n && (n = r), Xs(s), Yl(s)) : t = s, s.dep.activeLink = s.prevActiveLink, s.prevActiveLink = void 0, s = r } e.deps = t, e.depsTail = n } function Zs(e) { for (let t = e.deps; t; t = t.nextDep)if (t.dep.version !== t.version || t.dep.computed && (hi(t.dep.computed) || t.dep.version !== t.version)) return !0; return !!e._dirty } function hi(e) { if (e.flags & 4 && !(e.flags & 16) || (e.flags &= -17, e.globalVersion === On) || (e.globalVersion = On, !e.isSSR && e.flags & 128 && (!e.deps && !e._dirty || !Zs(e)))) return; e.flags |= 2; const t = e.dep, n = Te, s = xt; Te = e, xt = !0; try { pi(e); const r = e.fn(e._value); (t.version === 0 || Vt(r, e._value)) && (e.flags |= 128, e._value = r, t.version++) } catch (r) { throw t.version++, r } finally { Te = n, xt = s, di(e), e.flags &= -3 } } function Xs(e, t = !1) { const { dep: n, prevSub: s, nextSub: r } = e; if (s && (s.nextSub = r, e.prevSub = void 0), r && (r.prevSub = s, e.nextSub = void 0), n.subs === e && (n.subs = s, !s && n.computed)) { n.computed.flags &= -5; for (let i = n.computed.deps; i; i = i.nextDep)Xs(i, !0) } !t && !--n.sc && n.map && n.map.delete(n.key) } function Yl(e) { const { prevDep: t, nextDep: n } = e; t && (t.nextDep = n, e.prevDep = void 0), n && (n.prevDep = t, e.nextDep = void 0) } let xt = !0; const gi = []; function It() { gi.push(xt), xt = !1 } function Mt() { const e = gi.pop(); xt = e === void 0 ? !0 : e } function mi(e) { const { cleanup: t } = e; if (e.cleanup = void 0, t) { const n = Te; Te = void 0; try { t() } finally { Te = n } } } let On = 0; class Zl { constructor(t, n) { this.sub = t, this.dep = n, this.version = n.version, this.nextDep = this.prevDep = this.nextSub = this.prevSub = this.prevActiveLink = void 0 } } class Qs { constructor(t) { this.computed = t, this.version = 0, this.activeLink = void 0, this.subs = void 0, this.map = void 0, this.key = void 0, this.sc = 0, this.__v_skip = !0 } track(t) { if (!Te || !xt || Te === this.computed) return; let n = this.activeLink; if (n === void 0 || n.sub !== Te) n = this.activeLink = new Zl(Te, this), Te.deps ? (n.prevDep = Te.depsTail, Te.depsTail.nextDep = n, Te.depsTail = n) : Te.deps = Te.depsTail = n, bi(n); else if (n.version === -1 && (n.version = this.version, n.nextDep)) { const s = n.nextDep; s.prevDep = n.prevDep, n.prevDep && (n.prevDep.nextDep = s), n.prevDep = Te.depsTail, n.nextDep = void 0, Te.depsTail.nextDep = n, Te.depsTail = n, Te.deps === n && (Te.deps = s) } return n } trigger(t) { this.version++, On++, this.notify(t) } notify(t) { Ks(); try { for (let n = this.subs; n; n = n.prevSub)n.sub.notify() && n.sub.dep.notify() } finally { Ys() } } } function bi(e) { if (e.dep.sc++, e.sub.flags & 4) { const t = e.dep.computed; if (t && !e.dep.subs) { t.flags |= 20; for (let s = t.deps; s; s = s.nextDep)bi(s) } const n = e.dep.subs; n !== e && (e.prevSub = n, n && (n.nextSub = e)), e.dep.subs = e } } const Js = new WeakMap, tn = Symbol(""), er = Symbol(""), Ln = Symbol(""); function je(e, t, n) { if (xt && Te) { let s = Js.get(e); s || Js.set(e, s = new Map); let r = s.get(n); r || (s.set(n, r = new Qs), r.map = s, r.key = n), r.track() } } function Ot(e, t, n, s, r, i) { const o = Js.get(e); if (!o) { On++; return } const a = l => { l && l.trigger() }; if (Ks(), t === "clear") o.forEach(a); else { const l = Q(e), f = l && zs(n); if (l && n === "length") { const u = Number(s); o.forEach((d, g) => { (g === "length" || g === Ln || !Ut(g) && g >= u) && a(d) }) } else switch ((n !== void 0 || o.has(void 0)) && a(o.get(n)), f && a(o.get(Ln)), t) { case "add": l ? f && a(o.get("length")) : (a(o.get(tn)), hn(e) && a(o.get(er))); break; case "delete": l || (a(o.get(tn)), hn(e) && a(o.get(er))); break; case "set": hn(e) && a(o.get(tn)); break } } Ys() } function gn(e) { const t = ue(e); return t === e ? t : (je(t, "iterate", Ln), ut(e) ? t : t.map(Be)) } function os(e) { return je(e = ue(e), "iterate", Ln), e } const Xl = { __proto__: null, [Symbol.iterator]() { return tr(this, Symbol.iterator, Be) }, concat(...e) { return gn(this).concat(...e.map(t => Q(t) ? gn(t) : t)) }, entries() { return tr(this, "entries", e => (e[1] = Be(e[1]), e)) }, every(e, t) { return Lt(this, "every", e, t, void 0, arguments) }, filter(e, t) { return Lt(this, "filter", e, t, n => n.map(Be), arguments) }, find(e, t) { return Lt(this, "find", e, t, Be, arguments) }, findIndex(e, t) { return Lt(this, "findIndex", e, t, void 0, arguments) }, findLast(e, t) { return Lt(this, "findLast", e, t, Be, arguments) }, findLastIndex(e, t) { return Lt(this, "findLastIndex", e, t, void 0, arguments) }, forEach(e, t) { return Lt(this, "forEach", e, t, void 0, arguments) }, includes(...e) { return nr(this, "includes", e) }, indexOf(...e) { return nr(this, "indexOf", e) }, join(e) { return gn(this).join(e) }, lastIndexOf(...e) { return nr(this, "lastIndexOf", e) }, map(e, t) { return Lt(this, "map", e, t, void 0, arguments) }, pop() { return $n(this, "pop") }, push(...e) { return $n(this, "push", e) }, reduce(e, ...t) { return xi(this, "reduce", e, t) }, reduceRight(e, ...t) { return xi(this, "reduceRight", e, t) }, shift() { return $n(this, "shift") }, some(e, t) { return Lt(this, "some", e, t, void 0, arguments) }, splice(...e) { return $n(this, "splice", e) }, toReversed() { return gn(this).toReversed() }, toSorted(e) { return gn(this).toSorted(e) }, toSpliced(...e) { return gn(this).toSpliced(...e) }, unshift(...e) { return $n(this, "unshift", e) }, values() { return tr(this, "values", Be) } }; function tr(e, t, n) { const s = os(e), r = s[t](); return s !== e && !ut(e) && (r._next = r.next, r.next = () => { const i = r._next(); return i.value && (i.value = n(i.value)), i }), r } const Ql = Array.prototype; function Lt(e, t, n, s, r, i) { const o = os(e), a = o !== e && !ut(e), l = o[t]; if (l !== Ql[t]) { const d = l.apply(e, i); return a ? Be(d) : d } let f = n; o !== e && (a ? f = function (d, g) { return n.call(this, Be(d), g, e) } : n.length > 2 && (f = function (d, g) { return n.call(this, d, g, e) })); const u = l.call(o, f, s); return a && r ? r(u) : u } function xi(e, t, n, s) { const r = os(e); let i = n; return r !== e && (ut(e) ? n.length > 3 && (i = function (o, a, l) { return n.call(this, o, a, l, e) }) : i = function (o, a, l) { return n.call(this, o, Be(a), l, e) }), r[t](i, ...s) } function nr(e, t, n) { const s = ue(e); je(s, "iterate", Ln); const r = s[t](...n); return (r === -1 || r === !1) && rr(n[0]) ? (n[0] = ue(n[0]), s[t](...n)) : r } function $n(e, t, n = []) { It(), Ks(); const s = ue(e)[t].apply(e, n); return Ys(), Mt(), s } const Jl = Pt("__proto__,__v_isRef,__isVue"), yi = new Set(Object.getOwnPropertyNames(Symbol).filter(e => e !== "arguments" && e !== "caller").map(e => Symbol[e]).filter(Ut)); function ea(e) { Ut(e) || (e = String(e)); const t = ue(this); return je(t, "has", e), t.hasOwnProperty(e) } class wi { constructor(t = !1, n = !1) { this._isReadonly = t, this._isShallow = n } get(t, n, s) { if (n === "__v_skip") return t.__v_skip; const r = this._isReadonly, i = this._isShallow; if (n === "__v_isReactive") return !r; if (n === "__v_isReadonly") return r; if (n === "__v_isShallow") return i; if (n === "__v_raw") return s === (r ? i ? Ei : Si : i ? ki : Ti).get(t) || Object.getPrototypeOf(t) === Object.getPrototypeOf(s) ? t : void 0; const o = Q(t); if (!r) { let l; if (o && (l = Xl[n])) return l; if (n === "hasOwnProperty") return ea } const a = Reflect.get(t, n, Ve(t) ? t : s); return (Ut(n) ? yi.has(n) : Jl(n)) || (r || je(t, "get", n), i) ? a : Ve(a) ? o && zs(n) ? a : a.value : Re(a) ? r ? Ai(a) : cs(a) : a } } class vi extends wi { constructor(t = !1) { super(!1, t) } set(t, n, s, r) { let i = t[n]; if (!this._isShallow) { const l = qt(i); if (!ut(s) && !qt(s) && (i = ue(i), s = ue(s)), !Q(t) && Ve(i) && !Ve(s)) return l ? !1 : (i.value = s, !0) } const o = Q(t) && zs(n) ? Number(n) < t.length : me(t, n), a = Reflect.set(t, n, s, Ve(t) ? t : r); return t === ue(r) && (o ? Vt(s, i) && Ot(t, "set", n, s) : Ot(t, "add", n, s)), a } deleteProperty(t, n) { const s = me(t, n); t[n]; const r = Reflect.deleteProperty(t, n); return r && s && Ot(t, "delete", n, void 0), r } has(t, n) { const s = Reflect.has(t, n); return (!Ut(n) || !yi.has(n)) && je(t, "has", n), s } ownKeys(t) { return je(t, "iterate", Q(t) ? "length" : tn), Reflect.ownKeys(t) } } class _i extends wi { constructor(t = !1) { super(!0, t) } set(t, n) { return !0 } deleteProperty(t, n) { return !0 } } const ta = new vi, na = new _i, sa = new vi(!0), ra = new _i(!0), sr = e => e, ls = e => Reflect.getPrototypeOf(e); function ia(e, t, n) { return function (...s) { const r = this.__v_raw, i = ue(r), o = hn(i), a = e === "entries" || e === Symbol.iterator && o, l = e === "keys" && o, f = r[e](...s), u = n ? sr : t ? ps : Be; return !t && je(i, "iterate", l ? er : tn), { next() { const { value: d, done: g } = f.next(); return g ? { value: d, done: g } : { value: a ? [u(d[0]), u(d[1])] : u(d), done: g } }, [Symbol.iterator]() { return this } } } } function as(e) { return function (...t) { return e === "delete" ? !1 : e === "clear" ? void 0 : this } } function oa(e, t) { const n = { get(r) { const i = this.__v_raw, o = ue(i), a = ue(r); e || (Vt(r, a) && je(o, "get", r), je(o, "get", a)); const { has: l } = ls(o), f = t ? sr : e ? ps : Be; if (l.call(o, r)) return f(i.get(r)); if (l.call(o, a)) return f(i.get(a)); i !== o && i.get(r) }, get size() { const r = this.__v_raw; return !e && je(ue(r), "iterate", tn), Reflect.get(r, "size", r) }, has(r) { const i = this.__v_raw, o = ue(i), a = ue(r); return e || (Vt(r, a) && je(o, "has", r), je(o, "has", a)), r === a ? i.has(r) : i.has(r) || i.has(a) }, forEach(r, i) { const o = this, a = o.__v_raw, l = ue(a), f = t ? sr : e ? ps : Be; return !e && je(l, "iterate", tn), a.forEach((u, d) => r.call(i, f(u), f(d), o)) } }; return bt(n, e ? { add: as("add"), set: as("set"), delete: as("delete"), clear: as("clear") } : { add(r) { !t && !ut(r) && !qt(r) && (r = ue(r)); const i = ue(this); return ls(i).has.call(i, r) || (i.add(r), Ot(i, "add", r, r)), this }, set(r, i) { !t && !ut(i) && !qt(i) && (i = ue(i)); const o = ue(this), { has: a, get: l } = ls(o); let f = a.call(o, r); f || (r = ue(r), f = a.call(o, r)); const u = l.call(o, r); return o.set(r, i), f ? Vt(i, u) && Ot(o, "set", r, i) : Ot(o, "add", r, i), this }, delete(r) { const i = ue(this), { has: o, get: a } = ls(i); let l = o.call(i, r); l || (r = ue(r), l = o.call(i, r)), a && a.call(i, r); const f = i.delete(r); return l && Ot(i, "delete", r, void 0), f }, clear() { const r = ue(this), i = r.size !== 0, o = r.clear(); return i && Ot(r, "clear", void 0, void 0), o } }), ["keys", "values", "entries", Symbol.iterator].forEach(r => { n[r] = ia(r, e, t) }), n } function us(e, t) { const n = oa(e, t); return (s, r, i) => r === "__v_isReactive" ? !e : r === "__v_isReadonly" ? e : r === "__v_raw" ? s : Reflect.get(me(n, r) && r in s ? n : s, r, i) } const la = { get: us(!1, !1) }, aa = { get: us(!1, !0) }, ua = { get: us(!0, !1) }, ca = { get: us(!0, !0) }, Ti = new WeakMap, ki = new WeakMap, Si = new WeakMap, Ei = new WeakMap; function fa(e) { switch (e) { case "Object": case "Array": return 1; case "Map": case "Set": case "WeakMap": case "WeakSet": return 2; default: return 0 } } function pa(e) { return e.__v_skip || !Object.isExtensible(e) ? 0 : fa(Fl(e)) } function cs(e) { return qt(e) ? e : fs(e, !1, ta, la, Ti) } function da(e) { return fs(e, !1, sa, aa, ki) } function Ai(e) { return fs(e, !0, na, ua, Si) } function kd(e) { return fs(e, !0, ra, ca, Ei) } function fs(e, t, n, s, r) { if (!Re(e) || e.__v_raw && !(t && e.__v_isReactive)) return e; const i = pa(e); if (i === 0) return e; const o = r.get(e); if (o) return o; const a = new Proxy(e, i === 2 ? s : n); return r.set(e, a), a } function mn(e) { return qt(e) ? mn(e.__v_raw) : !!(e && e.__v_isReactive) } function qt(e) { return !!(e && e.__v_isReadonly) } function ut(e) { return !!(e && e.__v_isShallow) } function rr(e) { return e ? !!e.__v_raw : !1 } function ue(e) { const t = e && e.__v_raw; return t ? ue(t) : e } function ha(e) { return !me(e, "__v_skip") && Object.isExtensible(e) && js(e, "__v_skip", !0), e } const Be = e => Re(e) ? cs(e) : e, ps = e => Re(e) ? Ai(e) : e; function Ve(e) { return e ? e.__v_isRef === !0 : !1 } function J(e) { return ga(e, !1) } function ga(e, t) { return Ve(e) ? e : new ma(e, t) } class ma { constructor(t, n) { this.dep = new Qs, this.__v_isRef = !0, this.__v_isShallow = !1, this._rawValue = n ? t : ue(t), this._value = n ? t : Be(t), this.__v_isShallow = n } get value() { return this.dep.track(), this._value } set value(t) { const n = this._rawValue, s = this.__v_isShallow || ut(t) || qt(t); t = s ? t : ue(t), Vt(t, n) && (this._rawValue = t, this._value = s ? t : Be(t), this.dep.trigger()) } } function ba(e) { return Ve(e) ? e.value : e } const xa = { get: (e, t, n) => t === "__v_raw" ? e : ba(Reflect.get(e, t, n)), set: (e, t, n, s) => { const r = e[t]; return Ve(r) && !Ve(n) ? (r.value = n, !0) : Reflect.set(e, t, n, s) } }; function Ri(e) { return mn(e) ? e : new Proxy(e, xa) } class ya { constructor(t, n, s) { this.fn = t, this.setter = n, this._value = void 0, this.dep = new Qs(this), this.__v_isRef = !0, this.deps = void 0, this.depsTail = void 0, this.flags = 16, this.globalVersion = On - 1, this.next = void 0, this.effect = this, this.__v_isReadonly = !n, this.isSSR = s } notify() { if (this.flags |= 16, !(this.flags & 8) && Te !== this) return fi(this, !0), !0 } get value() { const t = this.dep.track(); return hi(this), t && (t.version = this.dep.version), this._value } set value(t) { this.setter && this.setter(t) } } function wa(e, t, n = !1) { let s, r; return be(e) ? s = e : (s = e.get, r = e.set), new ya(s, r, n) } const ds = {}, hs = new WeakMap; let nn; function va(e, t = !1, n = nn) { if (n) { let s = hs.get(n); s || hs.set(n, s = []), s.push(e) } } function _a(e, t, n = oe) { const { immediate: s, deep: r, once: i, scheduler: o, augmentJob: a, call: l } = n, f = $ => r ? $ : ut($) || r === !1 || r === 0 ? $t($, 1) : $t($); let u, d, g, y, E = !1, x = !1; if (Ve(e) ? (d = () => e.value, E = ut(e)) : mn(e) ? (d = () => f(e), E = !0) : Q(e) ? (x = !0, E = e.some($ => mn($) || ut($)), d = () => e.map($ => { if (Ve($)) return $.value; if (mn($)) return f($); if (be($)) return l ? l($, 2) : $() })) : be(e) ? t ? d = l ? () => l(e, 2) : e : d = () => { if (g) { It(); try { g() } finally { Mt() } } const $ = nn; nn = u; try { return l ? l(e, 3, [y]) : e(y) } finally { nn = $ } } : d = Jt, t && r) { const $ = d, z = r === !0 ? 1 / 0 : r; d = () => $t($(), z) } const M = Kl(), m = () => { u.stop(), M && M.active && Jr(M.effects, u) }; if (i && t) { const $ = t; t = (...z) => { $(...z), m() } } let H = x ? new Array(e.length).fill(ds) : ds; const Y = $ => { if (!(!(u.flags & 1) || !u.dirty && !$)) if (t) { const z = u.run(); if (r || E || (x ? z.some((O, P) => Vt(O, H[P])) : Vt(z, H))) { g && g(); const O = nn; nn = u; try { const P = [z, H === ds ? void 0 : x && H[0] === ds ? [] : H, y]; H = z, l ? l(t, 3, P) : t(...P) } finally { nn = O } } } else u.run() }; return a && a(Y), u = new ui(d), u.scheduler = o ? () => o(Y, !1) : Y, y = $ => va($, !1, u), g = u.onStop = () => { const $ = hs.get(u); if ($) { if (l) l($, 4); else for (const z of $) z(); hs.delete(u) } }, t ? s ? Y(!0) : H = u.run() : o ? o(Y.bind(null, !0), !0) : u.run(), m.pause = u.pause.bind(u), m.resume = u.resume.bind(u), m.stop = m, m } function $t(e, t = 1 / 0, n) { if (t <= 0 || !Re(e) || e.__v_skip || (n = n || new Set, n.has(e))) return e; if (n.add(e), t--, Ve(e)) $t(e.value, t, n); else if (Q(e)) for (let s = 0; s < e.length; s++)$t(e[s], t, n); else if (ei(e) || hn(e)) e.forEach(s => { $t(s, t, n) }); else if (si(e)) { for (const s in e) $t(e[s], t, n); for (const s of Object.getOwnPropertySymbols(e)) Object.prototype.propertyIsEnumerable.call(e, s) && $t(e[s], t, n) } return e } const Dn = []; let ir = !1; function Sd(e, ...t) { + if (ir) return; ir = !0, It(); const n = Dn.length ? Dn[Dn.length - 1].component : null, s = n && n.appContext.config.warnHandler, r = Ta(); if (s) bn(s, n, 11, [e + t.map(i => { var o, a; return (a = (o = i.toString) == null ? void 0 : o.call(i)) != null ? a : JSON.stringify(i) }).join(""), n && n.proxy, r.map(({ vnode: i }) => `at <${go(n, i.type)}>`).join(` +`), r]); else { + const i = [`[Vue warn]: ${e}`, ...t]; r.length && i.push(` +`, ...ka(r)) + } Mt(), ir = !1 + } function Ta() { let e = Dn[Dn.length - 1]; if (!e) return []; const t = []; for (; e;) { const n = t[0]; n && n.vnode === e ? n.recurseCount++ : t.push({ vnode: e, recurseCount: 0 }); const s = e.component && e.component.parent; e = s && s.vnode } return t } function ka(e) { + const t = []; return e.forEach((n, s) => { + t.push(...s === 0 ? [] : [` +`], ...Sa(n)) + }), t + } function Sa({ vnode: e, recurseCount: t }) { const n = t > 0 ? `... (${t} recursive calls)` : "", s = e.component ? e.component.parent == null : !1, r = ` at <${go(e.component, e.type, s)}`, i = ">" + n; return e.props ? [r, ...Ea(e.props), i] : [r + i] } function Ea(e) { const t = [], n = Object.keys(e); return n.slice(0, 3).forEach(s => { t.push(...Ci(s, e[s])) }), n.length > 3 && t.push(" ..."), t } function Ci(e, t, n) { return Pe(t) ? (t = JSON.stringify(t), n ? t : [`${e}=${t}`]) : typeof t == "number" || typeof t == "boolean" || t == null ? n ? t : [`${e}=${t}`] : Ve(t) ? (t = Ci(e, ue(t.value), !0), n ? t : [`${e}=Ref<`, t, ">"]) : be(t) ? [`${e}=fn${t.name ? `<${t.name}>` : ""}`] : (t = ue(t), n ? t : [`${e}=`, t]) } function bn(e, t, n, s) { try { return s ? e(...s) : e() } catch (r) { gs(r, t, n) } } function Tt(e, t, n, s) { if (be(e)) { const r = bn(e, t, n, s); return r && ti(r) && r.catch(i => { gs(i, t, n) }), r } if (Q(e)) { const r = []; for (let i = 0; i < e.length; i++)r.push(Tt(e[i], t, n, s)); return r } } function gs(e, t, n, s = !0) { const r = t ? t.vnode : null, { errorHandler: i, throwUnhandledErrorInProduction: o } = t && t.appContext.config || oe; if (t) { let a = t.parent; const l = t.proxy, f = `https://vuejs.org/error-reference/#runtime-${n}`; for (; a;) { const u = a.ec; if (u) { for (let d = 0; d < u.length; d++)if (u[d](e, l, f) === !1) return } a = a.parent } if (i) { It(), bn(i, null, 10, [e, l, f]), Mt(); return } } Aa(e, n, r, s, o) } function Aa(e, t, n, s = !0, r = !1) { if (r) throw e } const Ke = []; let kt = -1; const xn = []; let Wt = null, yn = 0; const Pi = Promise.resolve(); let ms = null; function St(e) { const t = ms || Pi; return e ? t.then(this ? e.bind(this) : e) : t } function Ra(e) { let t = kt + 1, n = Ke.length; for (; t < n;) { const s = t + n >>> 1, r = Ke[s], i = Nn(r); i < e || i === e && r.flags & 2 ? t = s + 1 : n = s } return t } function or(e) { if (!(e.flags & 1)) { const t = Nn(e), n = Ke[Ke.length - 1]; !n || !(e.flags & 2) && t >= Nn(n) ? Ke.push(e) : Ke.splice(Ra(t), 0, e), e.flags |= 1, Ii() } } function Ii() { ms || (ms = Pi.then(Li)) } function Ca(e) { Q(e) ? xn.push(...e) : Wt && e.id === -1 ? Wt.splice(yn + 1, 0, e) : e.flags & 1 || (xn.push(e), e.flags |= 1), Ii() } function Mi(e, t, n = kt + 1) { for (; n < Ke.length; n++) { const s = Ke[n]; if (s && s.flags & 2) { if (e && s.id !== e.uid) continue; Ke.splice(n, 1), n--, s.flags & 4 && (s.flags &= -2), s(), s.flags & 4 || (s.flags &= -2) } } } function Oi(e) { if (xn.length) { const t = [...new Set(xn)].sort((n, s) => Nn(n) - Nn(s)); if (xn.length = 0, Wt) { Wt.push(...t); return } for (Wt = t, yn = 0; yn < Wt.length; yn++) { const n = Wt[yn]; n.flags & 4 && (n.flags &= -2), n.flags & 8 || n(), n.flags &= -2 } Wt = null, yn = 0 } } const Nn = e => e.id == null ? e.flags & 2 ? -1 : 1 / 0 : e.id; function Li(e) { try { for (kt = 0; kt < Ke.length; kt++) { const t = Ke[kt]; t && !(t.flags & 8) && (t.flags & 4 && (t.flags &= -2), bn(t, t.i, t.i ? 15 : 14), t.flags & 4 || (t.flags &= -2)) } } finally { for (; kt < Ke.length; kt++) { const t = Ke[kt]; t && (t.flags &= -2) } kt = -1, Ke.length = 0, Oi(), ms = null, (Ke.length || xn.length) && Li() } } let ct = null, $i = null; function bs(e) { const t = ct; return ct = e, $i = e && e.type.__scopeId || null, t } function Di(e, t = ct, n) { if (!t || e._n) return e; const s = (...r) => { s._d && oo(-1); const i = bs(t); let o; try { o = e(...r) } finally { bs(i), s._d && oo(1) } return o }; return s._n = !0, s._c = !0, s._d = !0, s } function Pa(e, t) { if (ct === null) return e; const n = As(ct), s = e.dirs || (e.dirs = []); for (let r = 0; r < t.length; r++) { let [i, o, a, l = oe] = t[r]; i && (be(i) && (i = { mounted: i, updated: i }), i.deep && $t(o), s.push({ dir: i, instance: n, value: o, oldValue: void 0, arg: a, modifiers: l })) } return e } function sn(e, t, n, s) { const r = e.dirs, i = t && t.dirs; for (let o = 0; o < r.length; o++) { const a = r[o]; i && (a.oldValue = i[o].value); let l = a.dir[s]; l && (It(), Tt(l, n, 8, [e.el, a, e, t]), Mt()) } } const Ia = Symbol("_vte"), Ni = e => e.__isTeleport, Gt = Symbol("_leaveCb"), xs = Symbol("_enterCb"); function Ma() { const e = { isMounted: !1, isLeaving: !1, isUnmounting: !1, leavingVNodes: new Map }; return zn(() => { e.isMounted = !0 }), ys(() => { e.isUnmounting = !0 }), e } const ft = [Function, Array], Fi = { mode: String, appear: Boolean, persisted: Boolean, onBeforeEnter: ft, onEnter: ft, onAfterEnter: ft, onEnterCancelled: ft, onBeforeLeave: ft, onLeave: ft, onAfterLeave: ft, onLeaveCancelled: ft, onBeforeAppear: ft, onAppear: ft, onAfterAppear: ft, onAppearCancelled: ft }, Bi = e => { const t = e.subTree; return t.component ? Bi(t.component) : t }, Oa = { name: "BaseTransition", props: Fi, setup(e, { slots: t }) { const n = uo(), s = Ma(); return () => { const r = t.default && ji(t.default(), !0); if (!r || !r.length) return; const i = Hi(r), o = ue(e), { mode: a } = o; if (s.isLeaving) return ar(i); const l = Ui(i); if (!l) return ar(i); let f = lr(l, o, s, n, d => f = d); l.type !== Ye && Fn(l, f); let u = n.subTree && Ui(n.subTree); if (u && u.type !== Ye && !ln(l, u) && Bi(n).type !== Ye) { let d = lr(u, o, s, n); if (Fn(u, d), a === "out-in" && l.type !== Ye) return s.isLeaving = !0, d.afterLeave = () => { s.isLeaving = !1, n.job.flags & 8 || n.update(), delete d.afterLeave, u = void 0 }, ar(i); a === "in-out" && l.type !== Ye ? d.delayLeave = (g, y, E) => { const x = zi(s, u); x[String(u.key)] = u, g[Gt] = () => { y(), g[Gt] = void 0, delete f.delayedLeave, u = void 0 }, f.delayedLeave = () => { E(), delete f.delayedLeave, u = void 0 } } : u = void 0 } else u && (u = void 0); return i } } }; function Hi(e) { let t = e[0]; if (e.length > 1) { for (const n of e) if (n.type !== Ye) { t = n; break } } return t } const La = Oa; function zi(e, t) { const { leavingVNodes: n } = e; let s = n.get(t.type); return s || (s = Object.create(null), n.set(t.type, s)), s } function lr(e, t, n, s, r) { const { appear: i, mode: o, persisted: a = !1, onBeforeEnter: l, onEnter: f, onAfterEnter: u, onEnterCancelled: d, onBeforeLeave: g, onLeave: y, onAfterLeave: E, onLeaveCancelled: x, onBeforeAppear: M, onAppear: m, onAfterAppear: H, onAppearCancelled: Y } = t, $ = String(e.key), z = zi(n, e), O = (V, se) => { V && Tt(V, s, 9, se) }, P = (V, se) => { const fe = se[1]; O(V, se), Q(V) ? V.every(D => D.length <= 1) && fe() : V.length <= 1 && fe() }, ee = { mode: o, persisted: a, beforeEnter(V) { let se = l; if (!n.isMounted) if (i) se = M || l; else return; V[Gt] && V[Gt](!0); const fe = z[$]; fe && ln(e, fe) && fe.el[Gt] && fe.el[Gt](), O(se, [V]) }, enter(V) { let se = f, fe = u, D = d; if (!n.isMounted) if (i) se = m || f, fe = H || u, D = Y || d; else return; let Z = !1; const Se = V[xs] = Ue => { Z || (Z = !0, Ue ? O(D, [V]) : O(fe, [V]), ee.delayedLeave && ee.delayedLeave(), V[xs] = void 0) }; se ? P(se, [V, Se]) : Se() }, leave(V, se) { const fe = String(e.key); if (V[xs] && V[xs](!0), n.isUnmounting) return se(); O(g, [V]); let D = !1; const Z = V[Gt] = Se => { D || (D = !0, se(), Se ? O(x, [V]) : O(E, [V]), V[Gt] = void 0, z[fe] === e && delete z[fe]) }; z[fe] = e, y ? P(y, [V, Z]) : Z() }, clone(V) { const se = lr(V, t, n, s, r); return r && r(se), se } }; return ee } function ar(e) { if (ur(e)) return e = Kt(e), e.children = null, e } function Ui(e) { if (!ur(e)) return Ni(e.type) && e.children ? Hi(e.children) : e; if (e.component) return e.component.subTree; const { shapeFlag: t, children: n } = e; if (n) { if (t & 16) return n[0]; if (t & 32 && be(n.default)) return n.default() } } function Fn(e, t) { e.shapeFlag & 6 && e.component ? (e.transition = t, Fn(e.component.subTree, t)) : e.shapeFlag & 128 ? (e.ssContent.transition = t.clone(e.ssContent), e.ssFallback.transition = t.clone(e.ssFallback)) : e.transition = t } function ji(e, t = !1, n) { let s = [], r = 0; for (let i = 0; i < e.length; i++) { let o = e[i]; const a = n == null ? o.key : String(n) + String(o.key != null ? o.key : i); o.type === He ? (o.patchFlag & 128 && r++, s = s.concat(ji(o.children, t, a))) : (t || o.type !== Ye) && s.push(a != null ? Kt(o, { key: a }) : o) } if (r > 1) for (let i = 0; i < s.length; i++)s[i].patchFlag = -2; return s } function $a(e) { e.ids = [e.ids[0] + e.ids[2]++ + "-", 0, 0] } function Bn(e, t, n, s, r = !1) { if (Q(e)) { e.forEach((E, x) => Bn(E, t && (Q(t) ? t[x] : t), n, s, r)); return } if (Hn(s) && !r) { s.shapeFlag & 512 && s.type.__asyncResolved && s.component.subTree.component && Bn(e, t, n, s.component.subTree); return } const i = s.shapeFlag & 4 ? As(s.component) : s.el, o = r ? null : i, { i: a, r: l } = e, f = t && t.r, u = a.refs === oe ? a.refs = {} : a.refs, d = a.setupState, g = ue(d), y = d === oe ? () => !1 : E => me(g, E); if (f != null && f !== l && (Pe(f) ? (u[f] = null, y(f) && (d[f] = null)) : Ve(f) && (f.value = null)), be(l)) bn(l, a, 12, [o, u]); else { const E = Pe(l), x = Ve(l); if (E || x) { const M = () => { if (e.f) { const m = E ? y(l) ? d[l] : u[l] : l.value; r ? Q(m) && Jr(m, i) : Q(m) ? m.includes(i) || m.push(i) : E ? (u[l] = [i], y(l) && (d[l] = u[l])) : (l.value = [i], e.k && (u[e.k] = l.value)) } else E ? (u[l] = o, y(l) && (d[l] = o)) : x && (l.value = o, e.k && (u[e.k] = o)) }; o ? (M.id = -1, rt(M, n)) : M() } } } is().requestIdleCallback, is().cancelIdleCallback; const Hn = e => !!e.type.__asyncLoader, ur = e => e.type.__isKeepAlive; function Da(e, t, n = Zt, s = !1) { if (n) { const r = n[e] || (n[e] = []), i = t.__weh || (t.__weh = (...o) => { It(); const a = yr(n), l = Tt(t, n, e, o); return a(), Mt(), l }); return s ? r.unshift(i) : r.push(i), i } } const Vi = e => (t, n = Zt) => { (!qn || e === "sp") && Da(e, (...s) => t(...s), n) }, zn = Vi("m"), ys = Vi("bum"), Na = Symbol.for("v-ndc"); function ws(e, t, n, s) { let r; const i = n, o = Q(e); if (o || Pe(e)) { const a = o && mn(e); let l = !1, f = !1; a && (l = !ut(e), f = qt(e), e = os(e)), r = new Array(e.length); for (let u = 0, d = e.length; u < d; u++)r[u] = t(l ? f ? ps(Be(e[u])) : Be(e[u]) : e[u], u, void 0, i) } else if (typeof e == "number") { r = new Array(e); for (let a = 0; a < e; a++)r[a] = t(a + 1, a, void 0, i) } else if (Re(e)) if (e[Symbol.iterator]) r = Array.from(e, (a, l) => t(a, l, void 0, i)); else { const a = Object.keys(e); r = new Array(a.length); for (let l = 0, f = a.length; l < f; l++) { const u = a[l]; r[l] = t(e[u], u, l, i) } } else r = []; return r } const cr = e => e ? fo(e) ? As(e) : cr(e.parent) : null, Un = bt(Object.create(null), { $: e => e, $el: e => e.vnode.el, $data: e => e.data, $props: e => e.props, $attrs: e => e.attrs, $slots: e => e.slots, $refs: e => e.refs, $parent: e => cr(e.parent), $root: e => cr(e.root), $host: e => e.ce, $emit: e => e.emit, $options: e => e.type, $forceUpdate: e => e.f || (e.f = () => { or(e.update) }), $nextTick: e => e.n || (e.n = St.bind(e.proxy)), $watch: e => Jt }), fr = (e, t) => e !== oe && !e.__isScriptSetup && me(e, t), Fa = { get({ _: e }, t) { if (t === "__v_skip") return !0; const { ctx: n, setupState: s, data: r, props: i, accessCache: o, type: a, appContext: l } = e; let f; if (t[0] !== "$") { const y = o[t]; if (y !== void 0) switch (y) { case 1: return s[t]; case 2: return r[t]; case 4: return n[t]; case 3: return i[t] } else { if (fr(s, t)) return o[t] = 1, s[t]; if (r !== oe && me(r, t)) return o[t] = 2, r[t]; if ((f = e.propsOptions[0]) && me(f, t)) return o[t] = 3, i[t]; if (n !== oe && me(n, t)) return o[t] = 4, n[t]; o[t] = 0 } } const u = Un[t]; let d, g; if (u) return t === "$attrs" && je(e.attrs, "get", ""), u(e); if ((d = a.__cssModules) && (d = d[t])) return d; if (n !== oe && me(n, t)) return o[t] = 4, n[t]; if (g = l.config.globalProperties, me(g, t)) return g[t] }, set({ _: e }, t, n) { const { data: s, setupState: r, ctx: i } = e; return fr(r, t) ? (r[t] = n, !0) : s !== oe && me(s, t) ? (s[t] = n, !0) : me(e.props, t) || t[0] === "$" && t.slice(1) in e ? !1 : (i[t] = n, !0) }, has({ _: { data: e, setupState: t, accessCache: n, ctx: s, appContext: r, propsOptions: i } }, o) { let a; return !!n[o] || e !== oe && me(e, o) || fr(t, o) || (a = i[0]) && me(a, o) || me(s, o) || me(Un, o) || me(r.config.globalProperties, o) }, defineProperty(e, t, n) { return n.get != null ? e._.accessCache[t] = 0 : me(n, "value") && this.set(e, t, n.value, null), Reflect.defineProperty(e, t, n) } }; function qi() { return { app: null, config: { isNativeTag: Dl, performance: !1, globalProperties: {}, optionMergeStrategies: {}, errorHandler: void 0, warnHandler: void 0, compilerOptions: {} }, mixins: [], components: {}, directives: {}, provides: Object.create(null), optionsCache: new WeakMap, propsCache: new WeakMap, emitsCache: new WeakMap } } let Ba = 0; function Ha(e, t) { return function (s, r = null) { be(s) || (s = bt({}, s)), r != null && !Re(r) && (r = null); const i = qi(), o = new WeakSet, a = []; let l = !1; const f = i.app = { _uid: Ba++, _component: s, _props: r, _container: null, _context: i, _instance: null, version: Su, get config() { return i.config }, set config(u) { }, use(u, ...d) { return o.has(u) || (u && be(u.install) ? (o.add(u), u.install(f, ...d)) : be(u) && (o.add(u), u(f, ...d))), f }, mixin(u) { return f }, component(u, d) { return d ? (i.components[u] = d, f) : i.components[u] }, directive(u, d) { return d ? (i.directives[u] = d, f) : i.directives[u] }, mount(u, d, g) { if (!l) { const y = f._ceVNode || Oe(s, r); return y.appContext = i, g === !0 ? g = "svg" : g === !1 && (g = void 0), e(y, u, g), l = !0, f._container = u, u.__vue_app__ = f, As(y.component) } }, onUnmount(u) { a.push(u) }, unmount() { l && (Tt(a, f._instance, 16), e(null, f._container), delete f._container.__vue_app__) }, provide(u, d) { return i.provides[u] = d, f }, runWithContext(u) { const d = wn; wn = f; try { return u() } finally { wn = d } } }; return f } } let wn = null; function za(e, t, n = !1) { const s = uo(); if (s || wn) { let r = wn ? wn._context.provides : s ? s.parent == null || s.ce ? s.vnode.appContext && s.vnode.appContext.provides : s.parent.provides : void 0; if (r && e in r) return r[e]; if (arguments.length > 1) return n && be(t) ? t.call(s && s.proxy) : t } } const Wi = {}, Gi = () => Object.create(Wi), Ki = e => Object.getPrototypeOf(e) === Wi; function Ua(e, t, n, s = !1) { const r = {}, i = Gi(); e.propsDefaults = Object.create(null), Yi(e, t, r, i); for (const o in e.propsOptions[0]) o in r || (r[o] = void 0); n ? e.props = s ? r : da(r) : e.type.props ? e.props = r : e.props = i, e.attrs = i } function ja(e, t, n, s) { const { props: r, attrs: i, vnode: { patchFlag: o } } = e, a = ue(r), [l] = e.propsOptions; let f = !1; if ((s || o > 0) && !(o & 16)) { if (o & 8) { const u = e.vnode.dynamicProps; for (let d = 0; d < u.length; d++) { let g = u[d]; if (vs(e.emitsOptions, g)) continue; const y = t[g]; if (l) if (me(i, g)) y !== i[g] && (i[g] = y, f = !0); else { const E = jt(g); r[E] = pr(l, a, E, y, e, !1) } else y !== i[g] && (i[g] = y, f = !0) } } } else { Yi(e, t, r, i) && (f = !0); let u; for (const d in a) (!t || !me(t, d) && ((u = en(d)) === d || !me(t, u))) && (l ? n && (n[d] !== void 0 || n[u] !== void 0) && (r[d] = pr(l, a, d, void 0, e, !0)) : delete r[d]); if (i !== a) for (const d in i) (!t || !me(t, d)) && (delete i[d], f = !0) } f && Ot(e.attrs, "set", "") } function Yi(e, t, n, s) { const [r, i] = e.propsOptions; let o = !1, a; if (t) for (let l in t) { if (Pn(l)) continue; const f = t[l]; let u; r && me(r, u = jt(l)) ? !i || !i.includes(u) ? n[u] = f : (a || (a = {}))[u] = f : vs(e.emitsOptions, l) || (!(l in s) || f !== s[l]) && (s[l] = f, o = !0) } if (i) { const l = ue(n), f = a || oe; for (let u = 0; u < i.length; u++) { const d = i[u]; n[d] = pr(r, l, d, f[d], e, !me(f, d)) } } return o } function pr(e, t, n, s, r, i) { const o = e[n]; if (o != null) { const a = me(o, "default"); if (a && s === void 0) { const l = o.default; if (o.type !== Function && !o.skipFactory && be(l)) { const { propsDefaults: f } = r; if (n in f) s = f[n]; else { const u = yr(r); s = f[n] = l.call(null, t), u() } } else s = l; r.ce && r.ce._setProp(n, s) } o[0] && (i && !a ? s = !1 : o[1] && (s === "" || s === en(n)) && (s = !0)) } return s } function Va(e, t, n = !1) { const s = t.propsCache, r = s.get(e); if (r) return r; const i = e.props, o = {}, a = []; if (!i) return Re(e) && s.set(e, mt), mt; if (Q(i)) for (let f = 0; f < i.length; f++) { const u = jt(i[f]); Zi(u) && (o[u] = oe) } else if (i) for (const f in i) { const u = jt(f); if (Zi(u)) { const d = i[f], g = o[u] = Q(d) || be(d) ? { type: d } : bt({}, d), y = g.type; let E = !1, x = !0; if (Q(y)) for (let M = 0; M < y.length; ++M) { const m = y[M], H = be(m) && m.name; if (H === "Boolean") { E = !0; break } else H === "String" && (x = !1) } else E = be(y) && y.name === "Boolean"; g[0] = E, g[1] = x, (E || me(g, "default")) && a.push(u) } } const l = [o, a]; return Re(e) && s.set(e, l), l } function Zi(e) { return e[0] !== "$" && !Pn(e) } const dr = e => e === "_" || e === "__" || e === "_ctx" || e === "$stable", hr = e => Q(e) ? e.map(At) : [At(e)], qa = (e, t, n) => { if (t._n) return t; const s = Di((...r) => hr(t(...r)), n); return s._c = !1, s }, Xi = (e, t, n) => { const s = e._ctx; for (const r in e) { if (dr(r)) continue; const i = e[r]; if (be(i)) t[r] = qa(r, i, s); else if (i != null) { const o = hr(i); t[r] = () => o } } }, Qi = (e, t) => { const n = hr(t); e.slots.default = () => n }, Ji = (e, t, n) => { for (const s in t) (n || !dr(s)) && (e[s] = t[s]) }, Wa = (e, t, n) => { const s = e.slots = Gi(); if (e.vnode.shapeFlag & 32) { const r = t.__; r && js(s, "__", r, !0); const i = t._; i ? (Ji(s, t, n), n && js(s, "_", i, !0)) : Xi(t, s) } else t && Qi(e, t) }, Ga = (e, t, n) => { const { vnode: s, slots: r } = e; let i = !0, o = oe; if (s.shapeFlag & 32) { const a = t._; a ? n && a === 1 ? i = !1 : Ji(r, t, n) : (i = !t.$stable, Xi(t, r)), o = t } else t && (Qi(e, t), o = { default: 1 }); if (i) for (const a in r) !dr(a) && o[a] == null && delete r[a] }, rt = au; function Ka(e) { return Ya(e) } function Ya(e, t) { const n = is(); n.__VUE__ = !0; const { insert: s, remove: r, patchProp: i, createElement: o, createText: a, createComment: l, setText: f, setElementText: u, parentNode: d, nextSibling: g, setScopeId: y = Jt, insertStaticContent: E } = e, x = (c, h, b, k = null, v = null, _ = null, I = void 0, R = null, A = !!h.dynamicChildren) => { if (c === h) return; c && !ln(c, h) && (k = ye(c), de(c, v, _, !0), c = null), h.patchFlag === -2 && (A = !1, h.dynamicChildren = null); const { type: S, ref: U, shapeFlag: L } = h; switch (S) { case _s: M(c, h, b, k); break; case Ye: m(c, h, b, k); break; case mr: c == null && H(h, b, k, I); break; case He: D(c, h, b, k, v, _, I, R, A); break; default: L & 1 ? z(c, h, b, k, v, _, I, R, A) : L & 6 ? Z(c, h, b, k, v, _, I, R, A) : (L & 64 || L & 128) && S.process(c, h, b, k, v, _, I, R, A, Ce) }U != null && v ? Bn(U, c && c.ref, _, h || c, !h) : U == null && c && c.ref != null && Bn(c.ref, null, _, c, !0) }, M = (c, h, b, k) => { if (c == null) s(h.el = a(h.children), b, k); else { const v = h.el = c.el; h.children !== c.children && f(v, h.children) } }, m = (c, h, b, k) => { c == null ? s(h.el = l(h.children || ""), b, k) : h.el = c.el }, H = (c, h, b, k) => { [c.el, c.anchor] = E(c.children, h, b, k, c.el, c.anchor) }, Y = ({ el: c, anchor: h }, b, k) => { let v; for (; c && c !== h;)v = g(c), s(c, b, k), c = v; s(h, b, k) }, $ = ({ el: c, anchor: h }) => { let b; for (; c && c !== h;)b = g(c), r(c), c = b; r(h) }, z = (c, h, b, k, v, _, I, R, A) => { h.type === "svg" ? I = "svg" : h.type === "math" && (I = "mathml"), c == null ? O(h, b, k, v, _, I, R, A) : V(c, h, v, _, I, R, A) }, O = (c, h, b, k, v, _, I, R) => { let A, S; const { props: U, shapeFlag: L, transition: j, dirs: K } = c; if (A = c.el = o(c.type, _, U && U.is, U), L & 8 ? u(A, c.children) : L & 16 && ee(c.children, A, null, k, v, gr(c, _), I, R), K && sn(c, null, k, "created"), P(A, c, c.scopeId, I, k), U) { for (const he in U) he !== "value" && !Pn(he) && i(A, he, null, U[he], _, k); "value" in U && i(A, "value", null, U.value, _), (S = U.onVnodeBeforeMount) && Rt(S, k, c) } K && sn(c, null, k, "beforeMount"); const ne = Za(v, j); ne && j.beforeEnter(A), s(A, h, b), ((S = U && U.onVnodeMounted) || ne || K) && rt(() => { S && Rt(S, k, c), ne && j.enter(A), K && sn(c, null, k, "mounted") }, v) }, P = (c, h, b, k, v) => { if (b && y(c, b), k) for (let _ = 0; _ < k.length; _++)y(c, k[_]); if (v) { let _ = v.subTree; if (h === _ || io(_.type) && (_.ssContent === h || _.ssFallback === h)) { const I = v.vnode; P(c, I, I.scopeId, I.slotScopeIds, v.parent) } } }, ee = (c, h, b, k, v, _, I, R, A = 0) => { for (let S = A; S < c.length; S++) { const U = c[S] = R ? Yt(c[S]) : At(c[S]); x(null, U, h, b, k, v, _, I, R) } }, V = (c, h, b, k, v, _, I) => { const R = h.el = c.el; let { patchFlag: A, dynamicChildren: S, dirs: U } = h; A |= c.patchFlag & 16; const L = c.props || oe, j = h.props || oe; let K; if (b && rn(b, !1), (K = j.onVnodeBeforeUpdate) && Rt(K, b, h, c), U && sn(h, c, b, "beforeUpdate"), b && rn(b, !0), (L.innerHTML && j.innerHTML == null || L.textContent && j.textContent == null) && u(R, ""), S ? se(c.dynamicChildren, S, R, b, k, gr(h, v), _) : I || pe(c, h, R, null, b, k, gr(h, v), _, !1), A > 0) { if (A & 16) fe(R, L, j, b, v); else if (A & 2 && L.class !== j.class && i(R, "class", null, j.class, v), A & 4 && i(R, "style", L.style, j.style, v), A & 8) { const ne = h.dynamicProps; for (let he = 0; he < ne.length; he++) { const ie = ne[he], ke = L[ie], $e = j[ie]; ($e !== ke || ie === "value") && i(R, ie, ke, $e, v, b) } } A & 1 && c.children !== h.children && u(R, h.children) } else !I && S == null && fe(R, L, j, b, v); ((K = j.onVnodeUpdated) || U) && rt(() => { K && Rt(K, b, h, c), U && sn(h, c, b, "updated") }, k) }, se = (c, h, b, k, v, _, I) => { for (let R = 0; R < h.length; R++) { const A = c[R], S = h[R], U = A.el && (A.type === He || !ln(A, S) || A.shapeFlag & 198) ? d(A.el) : b; x(A, S, U, null, k, v, _, I, !0) } }, fe = (c, h, b, k, v) => { if (h !== b) { if (h !== oe) for (const _ in h) !Pn(_) && !(_ in b) && i(c, _, h[_], null, v, k); for (const _ in b) { if (Pn(_)) continue; const I = b[_], R = h[_]; I !== R && _ !== "value" && i(c, _, R, I, v, k) } "value" in b && i(c, "value", h.value, b.value, v) } }, D = (c, h, b, k, v, _, I, R, A) => { const S = h.el = c ? c.el : a(""), U = h.anchor = c ? c.anchor : a(""); let { patchFlag: L, dynamicChildren: j, slotScopeIds: K } = h; K && (R = R ? R.concat(K) : K), c == null ? (s(S, b, k), s(U, b, k), ee(h.children || [], b, U, v, _, I, R, A)) : L > 0 && L & 64 && j && c.dynamicChildren ? (se(c.dynamicChildren, j, b, v, _, I, R), (h.key != null || v && h === v.subTree) && eo(c, h, !0)) : pe(c, h, b, U, v, _, I, R, A) }, Z = (c, h, b, k, v, _, I, R, A) => { h.slotScopeIds = R, c == null ? h.shapeFlag & 512 ? v.ctx.activate(h, b, k, I, A) : Se(h, b, k, v, _, I, A) : Ue(c, h, A) }, Se = (c, h, b, k, v, _, I) => { const R = c.component = gu(c, k, v); if (ur(c) && (R.ctx.renderer = Ce), mu(R, !1, I), R.asyncDep) { if (v && v.registerDep(R, te, I), !c.el) { const A = R.subTree = Oe(Ye); m(null, A, h, b), c.placeholder = A.el } } else te(R, c, h, b, v, _, I) }, Ue = (c, h, b) => { const k = h.component = c.component; if (ou(c, h, b)) if (k.asyncDep && !k.asyncResolved) { C(k, h, b); return } else k.next = h, k.update(); else h.el = c.el, k.vnode = h }, te = (c, h, b, k, v, _, I) => { const R = () => { if (c.isMounted) { let { next: L, bu: j, u: K, parent: ne, vnode: he } = c; { const lt = to(c); if (lt) { L && (L.el = he.el, C(c, L, I)), lt.asyncDep.then(() => { c.isUnmounted || R() }); return } } let ie = L, ke; rn(c, !1), L ? (L.el = he.el, C(c, L, I)) : L = he, j && rs(j), (ke = L.props && L.props.onVnodeBeforeUpdate) && Rt(ke, ne, L, he), rn(c, !0); const $e = so(c), ot = c.subTree; c.subTree = $e, x(ot, $e, d(ot.el), ye(ot), c, v, _), L.el = $e.el, ie === null && lu(c, $e.el), K && rt(K, v), (ke = L.props && L.props.onVnodeUpdated) && rt(() => Rt(ke, ne, L, he), v) } else { let L; const { el: j, props: K } = h, { bm: ne, m: he, parent: ie, root: ke, type: $e } = c, ot = Hn(h); rn(c, !1), ne && rs(ne), !ot && (L = K && K.onVnodeBeforeMount) && Rt(L, ie, h), rn(c, !0); { ke.ce && ke.ce._def.shadowRoot !== !1 && ke.ce._injectChildStyle($e); const lt = c.subTree = so(c); x(null, lt, b, k, c, v, _), h.el = lt.el } if (he && rt(he, v), !ot && (L = K && K.onVnodeMounted)) { const lt = h; rt(() => Rt(L, ie, lt), v) } (h.shapeFlag & 256 || ie && Hn(ie.vnode) && ie.vnode.shapeFlag & 256) && c.a && rt(c.a, v), c.isMounted = !0, h = b = k = null } }; c.scope.on(); const A = c.effect = new ui(R); c.scope.off(); const S = c.update = A.run.bind(A), U = c.job = A.runIfDirty.bind(A); U.i = c, U.id = c.uid, A.scheduler = () => or(U), rn(c, !0), S() }, C = (c, h, b) => { h.component = c; const k = c.vnode.props; c.vnode = h, c.next = null, ja(c, h.props, k, b), Ga(c, h.children, b), It(), Mi(c), Mt() }, pe = (c, h, b, k, v, _, I, R, A = !1) => { const S = c && c.children, U = c ? c.shapeFlag : 0, L = h.children, { patchFlag: j, shapeFlag: K } = h; if (j > 0) { if (j & 128) { re(S, L, b, k, v, _, I, R, A); return } else if (j & 256) { Ge(S, L, b, k, v, _, I, R, A); return } } K & 8 ? (U & 16 && nt(S, v, _), L !== S && u(b, L)) : U & 16 ? K & 16 ? re(S, L, b, k, v, _, I, R, A) : nt(S, v, _, !0) : (U & 8 && u(b, ""), K & 16 && ee(L, b, k, v, _, I, R, A)) }, Ge = (c, h, b, k, v, _, I, R, A) => { c = c || mt, h = h || mt; const S = c.length, U = h.length, L = Math.min(S, U); let j; for (j = 0; j < L; j++) { const K = h[j] = A ? Yt(h[j]) : At(h[j]); x(c[j], K, b, null, v, _, I, R, A) } S > U ? nt(c, v, _, !0, !1, L) : ee(h, b, k, v, _, I, R, A, L) }, re = (c, h, b, k, v, _, I, R, A) => { let S = 0; const U = h.length; let L = c.length - 1, j = U - 1; for (; S <= L && S <= j;) { const K = c[S], ne = h[S] = A ? Yt(h[S]) : At(h[S]); if (ln(K, ne)) x(K, ne, b, null, v, _, I, R, A); else break; S++ } for (; S <= L && S <= j;) { const K = c[L], ne = h[j] = A ? Yt(h[j]) : At(h[j]); if (ln(K, ne)) x(K, ne, b, null, v, _, I, R, A); else break; L--, j-- } if (S > L) { if (S <= j) { const K = j + 1, ne = K < U ? h[K].el : k; for (; S <= j;)x(null, h[S] = A ? Yt(h[S]) : At(h[S]), b, ne, v, _, I, R, A), S++ } } else if (S > j) for (; S <= L;)de(c[S], v, _, !0), S++; else { const K = S, ne = S, he = new Map; for (S = ne; S <= j; S++) { const De = h[S] = A ? Yt(h[S]) : At(h[S]); De.key != null && he.set(De.key, S) } let ie, ke = 0; const $e = j - ne + 1; let ot = !1, lt = 0; const Bt = new Array($e); for (S = 0; S < $e; S++)Bt[S] = 0; for (S = K; S <= L; S++) { const De = c[S]; if (ke >= $e) { de(De, v, _, !0); continue } let Qe; if (De.key != null) Qe = he.get(De.key); else for (ie = ne; ie <= j; ie++)if (Bt[ie - ne] === 0 && ln(De, h[ie])) { Qe = ie; break } Qe === void 0 ? de(De, v, _, !0) : (Bt[Qe - ne] = S + 1, Qe >= lt ? lt = Qe : ot = !0, x(De, h[Qe], b, null, v, _, I, R, A), ke++) } const Jn = ot ? Xa(Bt) : mt; for (ie = Jn.length - 1, S = $e - 1; S >= 0; S--) { const De = ne + S, Qe = h[De], at = h[De + 1], Ht = De + 1 < U ? at.el || at.placeholder : k; Bt[S] === 0 ? x(null, Qe, b, Ht, v, _, I, R, A) : ot && (ie < 0 || S !== Jn[ie] ? B(Qe, b, Ht, 2) : ie--) } } }, B = (c, h, b, k, v = null) => { const { el: _, type: I, transition: R, children: A, shapeFlag: S } = c; if (S & 6) { B(c.component.subTree, h, b, k); return } if (S & 128) { c.suspense.move(h, b, k); return } if (S & 64) { I.move(c, h, b, Ce); return } if (I === He) { s(_, h, b); for (let L = 0; L < A.length; L++)B(A[L], h, b, k); s(c.anchor, h, b); return } if (I === mr) { Y(c, h, b); return } if (k !== 2 && S & 1 && R) if (k === 0) R.beforeEnter(_), s(_, h, b), rt(() => R.enter(_), v); else { const { leave: L, delayLeave: j, afterLeave: K } = R, ne = () => { c.ctx.isUnmounted ? r(_) : s(_, h, b) }, he = () => { L(_, () => { ne(), K && K() }) }; j ? j(_, ne, he) : he() } else s(_, h, b) }, de = (c, h, b, k = !1, v = !1) => { const { type: _, props: I, ref: R, children: A, dynamicChildren: S, shapeFlag: U, patchFlag: L, dirs: j, cacheIndex: K } = c; if (L === -2 && (v = !1), R != null && (It(), Bn(R, null, b, c, !0), Mt()), K != null && (h.renderCache[K] = void 0), U & 256) { h.ctx.deactivate(c); return } const ne = U & 1 && j, he = !Hn(c); let ie; if (he && (ie = I && I.onVnodeBeforeUnmount) && Rt(ie, h, c), U & 6) ge(c.component, b, k); else { if (U & 128) { c.suspense.unmount(b, k); return } ne && sn(c, null, h, "beforeUnmount"), U & 64 ? c.type.remove(c, h, b, Ce, k) : S && !S.hasOnce && (_ !== He || L > 0 && L & 64) ? nt(S, h, b, !1, !0) : (_ === He && L & 384 || !v && U & 16) && nt(A, h, b), k && ae(c) } (he && (ie = I && I.onVnodeUnmounted) || ne) && rt(() => { ie && Rt(ie, h, c), ne && sn(c, null, h, "unmounted") }, b) }, ae = c => { const { type: h, el: b, anchor: k, transition: v } = c; if (h === He) { ve(b, k); return } if (h === mr) { $(c); return } const _ = () => { r(b), v && !v.persisted && v.afterLeave && v.afterLeave() }; if (c.shapeFlag & 1 && v && !v.persisted) { const { leave: I, delayLeave: R } = v, A = () => I(b, _); R ? R(c.el, _, A) : A() } else _() }, ve = (c, h) => { let b; for (; c !== h;)b = g(c), r(c), c = b; r(h) }, ge = (c, h, b) => { const { bum: k, scope: v, job: _, subTree: I, um: R, m: A, a: S, parent: U, slots: { __: L } } = c; no(A), no(S), k && rs(k), U && Q(L) && L.forEach(j => { U.renderCache[j] = void 0 }), v.stop(), _ && (_.flags |= 8, de(I, c, h, b)), R && rt(R, h), rt(() => { c.isUnmounted = !0 }, h), h && h.pendingBranch && !h.isUnmounted && c.asyncDep && !c.asyncResolved && c.suspenseId === h.pendingId && (h.deps--, h.deps === 0 && h.resolve()) }, nt = (c, h, b, k = !1, v = !1, _ = 0) => { for (let I = _; I < c.length; I++)de(c[I], h, b, k, v) }, ye = c => { if (c.shapeFlag & 6) return ye(c.component.subTree); if (c.shapeFlag & 128) return c.suspense.next(); const h = g(c.anchor || c.el), b = h && h[Ia]; return b ? g(b) : h }; let ht = !1; const G = (c, h, b) => { c == null ? h._vnode && de(h._vnode, null, null, !0) : x(h._vnode || null, c, h, null, null, null, b), h._vnode = c, ht || (ht = !0, Mi(), Oi(), ht = !1) }, Ce = { p: x, um: de, m: B, r: ae, mt: Se, mc: ee, pc: pe, pbc: se, n: ye, o: e }; return { render: G, hydrate: void 0, createApp: Ha(G) } } function gr({ type: e, props: t }, n) { return n === "svg" && e === "foreignObject" || n === "mathml" && e === "annotation-xml" && t && t.encoding && t.encoding.includes("html") ? void 0 : n } function rn({ effect: e, job: t }, n) { n ? (e.flags |= 32, t.flags |= 4) : (e.flags &= -33, t.flags &= -5) } function Za(e, t) { return (!e || e && !e.pendingBranch) && t && !t.persisted } function eo(e, t, n = !1) { const s = e.children, r = t.children; if (Q(s) && Q(r)) for (let i = 0; i < s.length; i++) { const o = s[i]; let a = r[i]; a.shapeFlag & 1 && !a.dynamicChildren && ((a.patchFlag <= 0 || a.patchFlag === 32) && (a = r[i] = Yt(r[i]), a.el = o.el), !n && a.patchFlag !== -2 && eo(o, a)), a.type === _s && (a.el = o.el), a.type === Ye && !a.el && (a.el = o.el) } } function Xa(e) { const t = e.slice(), n = [0]; let s, r, i, o, a; const l = e.length; for (s = 0; s < l; s++) { const f = e[s]; if (f !== 0) { if (r = n[n.length - 1], e[r] < f) { t[s] = r, n.push(s); continue } for (i = 0, o = n.length - 1; i < o;)a = i + o >> 1, e[n[a]] < f ? i = a + 1 : o = a; f < e[n[i]] && (i > 0 && (t[s] = n[i - 1]), n[i] = s) } } for (i = n.length, o = n[i - 1]; i-- > 0;)n[i] = o, o = t[o]; return n } function to(e) { const t = e.subTree.component; if (t) return t.asyncDep && !t.asyncResolved ? t : to(t) } function no(e) { if (e) for (let t = 0; t < e.length; t++)e[t].flags |= 8 } const Qa = Symbol.for("v-scx"), Ja = () => za(Qa); function on(e, t, n) { return eu(e, t, n) } function eu(e, t, n = oe) { const { immediate: s, deep: r, flush: i, once: o } = n, a = bt({}, n), l = t && s || !t && i !== "post"; let f; if (qn) { if (i === "sync") { const y = Ja(); f = y.__watcherHandles || (y.__watcherHandles = []) } else if (!l) { const y = () => { }; return y.stop = Jt, y.resume = Jt, y.pause = Jt, y } } const u = Zt; a.call = (y, E, x) => Tt(y, u, E, x); let d = !1; i === "post" ? a.scheduler = y => { rt(y, u && u.suspense) } : i !== "sync" && (d = !0, a.scheduler = (y, E) => { E ? y() : or(y) }), a.augmentJob = y => { t && (y.flags |= 4), d && (y.flags |= 2, u && (y.id = u.uid, y.i = u)) }; const g = _a(e, t, a); return qn && (f ? f.push(g) : l && g()), g } const tu = (e, t) => t === "modelValue" || t === "model-value" ? e.modelModifiers : e[`${t}Modifiers`] || e[`${jt(t)}Modifiers`] || e[`${en(t)}Modifiers`]; function nu(e, t, ...n) { if (e.isUnmounted) return; const s = e.vnode.props || oe; let r = n; const i = t.startsWith("update:"), o = i && tu(s, t.slice(7)); o && (o.trim && (r = n.map(u => Pe(u) ? u.trim() : u)), o.number && (r = n.map(Vs))); let a, l = s[a = Us(t)] || s[a = Us(jt(t))]; !l && i && (l = s[a = Us(en(t))]), l && Tt(l, e, 6, r); const f = s[a + "Once"]; if (f) { if (!e.emitted) e.emitted = {}; else if (e.emitted[a]) return; e.emitted[a] = !0, Tt(f, e, 6, r) } } function su(e, t, n = !1) { const s = t.emitsCache, r = s.get(e); if (r !== void 0) return r; const i = e.emits; let o = {}; return i ? (Q(i) ? i.forEach(a => o[a] = null) : bt(o, i), Re(e) && s.set(e, o), o) : (Re(e) && s.set(e, null), null) } function vs(e, t) { return !e || !ts(t) ? !1 : (t = t.slice(2).replace(/Once$/, ""), me(e, t[0].toLowerCase() + t.slice(1)) || me(e, en(t)) || me(e, t)) } function Ed() { } function so(e) { const { type: t, vnode: n, proxy: s, withProxy: r, propsOptions: [i], slots: o, attrs: a, emit: l, render: f, renderCache: u, props: d, data: g, setupState: y, ctx: E, inheritAttrs: x } = e, M = bs(e); let m, H; try { if (n.shapeFlag & 4) { const $ = r || s, z = $; m = At(f.call(z, $, u, d, y, g, E)), H = a } else { const $ = t; m = At($.length > 1 ? $(d, { attrs: a, slots: o, emit: l }) : $(d, null)), H = t.props ? a : ru(a) } } catch ($) { jn.length = 0, gs($, e, 1), m = Oe(Ye) } let Y = m; if (H && x !== !1) { const $ = Object.keys(H), { shapeFlag: z } = Y; $.length && z & 7 && (i && $.some(Hs) && (H = iu(H, i)), Y = Kt(Y, H, !1, !0)) } return n.dirs && (Y = Kt(Y, null, !1, !0), Y.dirs = Y.dirs ? Y.dirs.concat(n.dirs) : n.dirs), n.transition && Fn(Y, n.transition), m = Y, bs(M), m } const ru = e => { let t; for (const n in e) (n === "class" || n === "style" || ts(n)) && ((t || (t = {}))[n] = e[n]); return t }, iu = (e, t) => { const n = {}; for (const s in e) (!Hs(s) || !(s.slice(9) in t)) && (n[s] = e[s]); return n }; function ou(e, t, n) { const { props: s, children: r, component: i } = e, { props: o, children: a, patchFlag: l } = t, f = i.emitsOptions; if (t.dirs || t.transition) return !0; if (n && l >= 0) { if (l & 1024) return !0; if (l & 16) return s ? ro(s, o, f) : !!o; if (l & 8) { const u = t.dynamicProps; for (let d = 0; d < u.length; d++) { const g = u[d]; if (o[g] !== s[g] && !vs(f, g)) return !0 } } } else return (r || a) && (!a || !a.$stable) ? !0 : s === o ? !1 : s ? o ? ro(s, o, f) : !0 : !!o; return !1 } function ro(e, t, n) { const s = Object.keys(t); if (s.length !== Object.keys(e).length) return !0; for (let r = 0; r < s.length; r++) { const i = s[r]; if (t[i] !== e[i] && !vs(n, i)) return !0 } return !1 } function lu({ vnode: e, parent: t }, n) { for (; t;) { const s = t.subTree; if (s.suspense && s.suspense.activeBranch === e && (s.el = e.el), s === e) (e = t.vnode).el = n, t = t.parent; else break } } const io = e => e.__isSuspense; function au(e, t) { t && t.pendingBranch ? Q(e) ? t.effects.push(...e) : t.effects.push(e) : Ca(e) } const He = Symbol.for("v-fgt"), _s = Symbol.for("v-txt"), Ye = Symbol.for("v-cmt"), mr = Symbol.for("v-stc"), jn = []; let it = null; function F(e = !1) { jn.push(it = e ? null : []) } function uu() { jn.pop(), it = jn[jn.length - 1] || null } let Vn = 1; function oo(e, t = !1) { Vn += e, e < 0 && it && t && (it.hasOnce = !0) } function lo(e) { return e.dynamicChildren = Vn > 0 ? it || mt : null, uu(), Vn > 0 && it && it.push(e), e } function W(e, t, n, s, r, i) { return lo(w(e, t, n, s, r, i, !0)) } function Et(e, t, n, s, r) { return lo(Oe(e, t, n, s, r, !0)) } function Ts(e) { return e ? e.__v_isVNode === !0 : !1 } function ln(e, t) { return e.type === t.type && e.key === t.key } const ao = ({ key: e }) => e != null ? e : null, ks = ({ ref: e, ref_key: t, ref_for: n }) => (typeof e == "number" && (e = "" + e), e != null ? Pe(e) || Ve(e) || be(e) ? { i: ct, r: e, k: t, f: !!n } : e : null); function w(e, t = null, n = null, s = 0, r = null, i = e === He ? 0 : 1, o = !1, a = !1) { const l = { __v_isVNode: !0, __v_skip: !0, type: e, props: t, key: t && ao(t), ref: t && ks(t), scopeId: $i, slotScopeIds: null, children: n, component: null, suspense: null, ssContent: null, ssFallback: null, dirs: null, transition: null, el: null, anchor: null, target: null, targetStart: null, targetAnchor: null, staticCount: 0, shapeFlag: i, patchFlag: s, dynamicProps: r, dynamicChildren: null, appContext: null, ctx: ct }; return a ? (br(l, n), i & 128 && e.normalize(l)) : n && (l.shapeFlag |= Pe(n) ? 8 : 16), Vn > 0 && !o && it && (l.patchFlag > 0 || i & 6) && l.patchFlag !== 32 && it.push(l), l } const Oe = cu; function cu(e, t = null, n = null, s = 0, r = null, i = !1) { if ((!e || e === Na) && (e = Ye), Ts(e)) { const a = Kt(e, t, !0); return n && br(a, n), Vn > 0 && !i && it && (a.shapeFlag & 6 ? it[it.indexOf(e)] = a : it.push(a)), a.patchFlag = -2, a } if (Tu(e) && (e = e.__vccOpts), t) { t = fu(t); let { class: a, style: l } = t; a && !Pe(a) && (t.class = we(a)), Re(l) && (rr(l) && !Q(l) && (l = bt({}, l)), t.style = qs(l)) } const o = Pe(e) ? 1 : io(e) ? 128 : Ni(e) ? 64 : Re(e) ? 4 : be(e) ? 2 : 0; return w(e, t, n, s, r, o, i, !0) } function fu(e) { return e ? rr(e) || Ki(e) ? bt({}, e) : e : null } function Kt(e, t, n = !1, s = !1) { const { props: r, ref: i, patchFlag: o, children: a, transition: l } = e, f = t ? pu(r || {}, t) : r, u = { __v_isVNode: !0, __v_skip: !0, type: e.type, props: f, key: f && ao(f), ref: t && t.ref ? n && i ? Q(i) ? i.concat(ks(t)) : [i, ks(t)] : ks(t) : i, scopeId: e.scopeId, slotScopeIds: e.slotScopeIds, children: a, target: e.target, targetStart: e.targetStart, targetAnchor: e.targetAnchor, staticCount: e.staticCount, shapeFlag: e.shapeFlag, patchFlag: t && e.type !== He ? o === -1 ? 16 : o | 16 : o, dynamicProps: e.dynamicProps, dynamicChildren: e.dynamicChildren, appContext: e.appContext, dirs: e.dirs, transition: l, component: e.component, suspense: e.suspense, ssContent: e.ssContent && Kt(e.ssContent), ssFallback: e.ssFallback && Kt(e.ssFallback), placeholder: e.placeholder, el: e.el, anchor: e.anchor, ctx: e.ctx, ce: e.ce }; return l && s && Fn(u, l.clone(u)), u } function Ss(e = " ", t = 0) { return Oe(_s, null, e, t) } function qe(e = "", t = !1) { return t ? (F(), Et(Ye, null, e)) : Oe(Ye, null, e) } function At(e) { return e == null || typeof e == "boolean" ? Oe(Ye) : Q(e) ? Oe(He, null, e.slice()) : Ts(e) ? Yt(e) : Oe(_s, null, String(e)) } function Yt(e) { return e.el === null && e.patchFlag !== -1 || e.memo ? e : Kt(e) } function br(e, t) { let n = 0; const { shapeFlag: s } = e; if (t == null) t = null; else if (Q(t)) n = 16; else if (typeof t == "object") if (s & 65) { const r = t.default; r && (r._c && (r._d = !1), br(e, r()), r._c && (r._d = !0)); return } else { n = 32; const r = t._; !r && !Ki(t) ? t._ctx = ct : r === 3 && ct && (ct.slots._ === 1 ? t._ = 1 : (t._ = 2, e.patchFlag |= 1024)) } else be(t) ? (t = { default: t, _ctx: ct }, n = 32) : (t = String(t), s & 64 ? (n = 16, t = [Ss(t)]) : n = 8); e.children = t, e.shapeFlag |= n } function pu(...e) { const t = {}; for (let n = 0; n < e.length; n++) { const s = e[n]; for (const r in s) if (r === "class") t.class !== s.class && (t.class = we([t.class, s.class])); else if (r === "style") t.style = qs([t.style, s.style]); else if (ts(r)) { const i = t[r], o = s[r]; o && i !== o && !(Q(i) && i.includes(o)) && (t[r] = i ? [].concat(i, o) : o) } else r !== "" && (t[r] = s[r]) } return t } function Rt(e, t, n, s = null) { Tt(e, t, 7, [n, s]) } const du = qi(); let hu = 0; function gu(e, t, n) { const s = e.type, r = (t ? t.appContext : e.appContext) || du, i = { uid: hu++, vnode: e, type: s, parent: t, appContext: r, root: null, next: null, subTree: null, effect: null, update: null, job: null, scope: new Gl(!0), render: null, proxy: null, exposed: null, exposeProxy: null, withProxy: null, provides: t ? t.provides : Object.create(r.provides), ids: t ? t.ids : ["", 0, 0], accessCache: null, renderCache: [], components: null, directives: null, propsOptions: Va(s, r), emitsOptions: su(s, r), emit: null, emitted: null, propsDefaults: oe, inheritAttrs: s.inheritAttrs, ctx: oe, data: oe, props: oe, attrs: oe, slots: oe, refs: oe, setupState: oe, setupContext: null, suspense: n, suspenseId: n ? n.pendingId : 0, asyncDep: null, asyncResolved: !1, isMounted: !1, isUnmounted: !1, isDeactivated: !1, bc: null, c: null, bm: null, m: null, bu: null, u: null, um: null, bum: null, da: null, a: null, rtg: null, rtc: null, ec: null, sp: null }; return i.ctx = { _: i }, i.root = t ? t.root : i, i.emit = nu.bind(null, i), e.ce && e.ce(i), i } let Zt = null; const uo = () => Zt || ct; let Es, xr; { const e = is(), t = (n, s) => { let r; return (r = e[n]) || (r = e[n] = []), r.push(s), i => { r.length > 1 ? r.forEach(o => o(i)) : r[0](i) } }; Es = t("__VUE_INSTANCE_SETTERS__", n => Zt = n), xr = t("__VUE_SSR_SETTERS__", n => qn = n) } const yr = e => { const t = Zt; return Es(e), e.scope.on(), () => { e.scope.off(), Es(t) } }, co = () => { Zt && Zt.scope.off(), Es(null) }; function fo(e) { return e.vnode.shapeFlag & 4 } let qn = !1; function mu(e, t = !1, n = !1) { t && xr(t); const { props: s, children: r } = e.vnode, i = fo(e); Ua(e, s, i, t), Wa(e, r, n || t); const o = i ? bu(e, t) : void 0; return t && xr(!1), o } function bu(e, t) { const n = e.type; e.accessCache = Object.create(null), e.proxy = new Proxy(e.ctx, Fa); const { setup: s } = n; if (s) { It(); const r = e.setupContext = s.length > 1 ? yu(e) : null, i = yr(e), o = bn(s, e, 0, [e.props, r]), a = ti(o); if (Mt(), i(), (a || e.sp) && !Hn(e) && $a(e), a) { if (o.then(co, co), t) return o.then(l => { po(e, l) }).catch(l => { gs(l, e, 0) }); e.asyncDep = o } else po(e, o) } else ho(e) } function po(e, t, n) { be(t) ? e.type.__ssrInlineRender ? e.ssrRender = t : e.render = t : Re(t) && (e.setupState = Ri(t)), ho(e) } function ho(e, t, n) { const s = e.type; e.render || (e.render = s.render || Jt) } const xu = { get(e, t) { return je(e, "get", ""), e[t] } }; function yu(e) { const t = n => { e.exposed = n || {} }; return { attrs: new Proxy(e.attrs, xu), slots: e.slots, emit: e.emit, expose: t } } function As(e) { return e.exposed ? e.exposeProxy || (e.exposeProxy = new Proxy(Ri(ha(e.exposed)), { get(t, n) { if (n in t) return t[n]; if (n in Un) return Un[n](e) }, has(t, n) { return n in t || n in Un } })) : e.proxy } const wu = /(?:^|[-_])(\w)/g, vu = e => e.replace(wu, t => t.toUpperCase()).replace(/[-_]/g, ""); function _u(e, t = !0) { return be(e) ? e.displayName || e.name : e.name || t && e.__name } function go(e, t, n = !1) { let s = _u(t); if (!s && t.__file) { const r = t.__file.match(/([^/\\]+)\.\w+$/); r && (s = r[1]) } if (!s && e && e.parent) { const r = i => { for (const o in i) if (i[o] === t) return o }; s = r(e.components || e.parent.type.components) || r(e.appContext.components) } return s ? vu(s) : n ? "App" : "Anonymous" } function Tu(e) { return be(e) && "__vccOpts" in e } const Ee = (e, t) => wa(e, t, qn); function ku(e, t, n) { const s = arguments.length; return s === 2 ? Re(t) && !Q(t) ? Ts(t) ? Oe(e, null, [t]) : Oe(e, t) : Oe(e, null, t) : (s > 3 ? n = Array.prototype.slice.call(arguments, 2) : s === 3 && Ts(n) && (n = [n]), Oe(e, t, n)) } const Su = "3.5.18"; let wr; const mo = typeof window != "undefined" && window.trustedTypes; if (mo) try { wr = mo.createPolicy("vue", { createHTML: e => e }) } catch { } const bo = wr ? e => wr.createHTML(e) : e => e, Eu = "http://www.w3.org/2000/svg", Au = "http://www.w3.org/1998/Math/MathML", Dt = typeof document != "undefined" ? document : null, xo = Dt && Dt.createElement("template"), Ru = { insert: (e, t, n) => { t.insertBefore(e, n || null) }, remove: e => { const t = e.parentNode; t && t.removeChild(e) }, createElement: (e, t, n, s) => { const r = t === "svg" ? Dt.createElementNS(Eu, e) : t === "mathml" ? Dt.createElementNS(Au, e) : n ? Dt.createElement(e, { is: n }) : Dt.createElement(e); return e === "select" && s && s.multiple != null && r.setAttribute("multiple", s.multiple), r }, createText: e => Dt.createTextNode(e), createComment: e => Dt.createComment(e), setText: (e, t) => { e.nodeValue = t }, setElementText: (e, t) => { e.textContent = t }, parentNode: e => e.parentNode, nextSibling: e => e.nextSibling, querySelector: e => Dt.querySelector(e), setScopeId(e, t) { e.setAttribute(t, "") }, insertStaticContent(e, t, n, s, r, i) { const o = n ? n.previousSibling : t.lastChild; if (r && (r === i || r.nextSibling)) for (; t.insertBefore(r.cloneNode(!0), n), !(r === i || !(r = r.nextSibling));); else { xo.innerHTML = bo(s === "svg" ? `${e}` : s === "mathml" ? `${e}` : e); const a = xo.content; if (s === "svg" || s === "mathml") { const l = a.firstChild; for (; l.firstChild;)a.appendChild(l.firstChild); a.removeChild(l) } t.insertBefore(a, n) } return [o ? o.nextSibling : t.firstChild, n ? n.previousSibling : t.lastChild] } }, Xt = "transition", Wn = "animation", Gn = Symbol("_vtc"), yo = { name: String, type: String, css: { type: Boolean, default: !0 }, duration: [String, Number, Object], enterFromClass: String, enterActiveClass: String, enterToClass: String, appearFromClass: String, appearActiveClass: String, appearToClass: String, leaveFromClass: String, leaveActiveClass: String, leaveToClass: String }, Cu = bt({}, Fi, yo), Pu = (e => (e.displayName = "Transition", e.props = Cu, e))((e, { slots: t }) => ku(La, Iu(e), t)), an = (e, t = []) => { Q(e) ? e.forEach(n => n(...t)) : e && e(...t) }, wo = e => e ? Q(e) ? e.some(t => t.length > 1) : e.length > 1 : !1; function Iu(e) { const t = {}; for (const D in e) D in yo || (t[D] = e[D]); if (e.css === !1) return t; const { name: n = "v", type: s, duration: r, enterFromClass: i = `${n}-enter-from`, enterActiveClass: o = `${n}-enter-active`, enterToClass: a = `${n}-enter-to`, appearFromClass: l = i, appearActiveClass: f = o, appearToClass: u = a, leaveFromClass: d = `${n}-leave-from`, leaveActiveClass: g = `${n}-leave-active`, leaveToClass: y = `${n}-leave-to` } = e, E = Mu(r), x = E && E[0], M = E && E[1], { onBeforeEnter: m, onEnter: H, onEnterCancelled: Y, onLeave: $, onLeaveCancelled: z, onBeforeAppear: O = m, onAppear: P = H, onAppearCancelled: ee = Y } = t, V = (D, Z, Se, Ue) => { D._enterCancelled = Ue, un(D, Z ? u : a), un(D, Z ? f : o), Se && Se() }, se = (D, Z) => { D._isLeaving = !1, un(D, d), un(D, y), un(D, g), Z && Z() }, fe = D => (Z, Se) => { const Ue = D ? P : H, te = () => V(Z, D, Se); an(Ue, [Z, te]), vo(() => { un(Z, D ? l : i), Nt(Z, D ? u : a), wo(Ue) || _o(Z, s, x, te) }) }; return bt(t, { onBeforeEnter(D) { an(m, [D]), Nt(D, i), Nt(D, o) }, onBeforeAppear(D) { an(O, [D]), Nt(D, l), Nt(D, f) }, onEnter: fe(!1), onAppear: fe(!0), onLeave(D, Z) { D._isLeaving = !0; const Se = () => se(D, Z); Nt(D, d), D._enterCancelled ? (Nt(D, g), So()) : (So(), Nt(D, g)), vo(() => { D._isLeaving && (un(D, d), Nt(D, y), wo($) || _o(D, s, M, Se)) }), an($, [D, Se]) }, onEnterCancelled(D) { V(D, !1, void 0, !0), an(Y, [D]) }, onAppearCancelled(D) { V(D, !0, void 0, !0), an(ee, [D]) }, onLeaveCancelled(D) { se(D), an(z, [D]) } }) } function Mu(e) { if (e == null) return null; if (Re(e)) return [vr(e.enter), vr(e.leave)]; { const t = vr(e); return [t, t] } } function vr(e) { return zl(e) } function Nt(e, t) { t.split(/\s+/).forEach(n => n && e.classList.add(n)), (e[Gn] || (e[Gn] = new Set)).add(t) } function un(e, t) { t.split(/\s+/).forEach(s => s && e.classList.remove(s)); const n = e[Gn]; n && (n.delete(t), n.size || (e[Gn] = void 0)) } function vo(e) { requestAnimationFrame(() => { requestAnimationFrame(e) }) } let Ou = 0; function _o(e, t, n, s) { const r = e._endId = ++Ou, i = () => { r === e._endId && s() }; if (n != null) return setTimeout(i, n); const { type: o, timeout: a, propCount: l } = Lu(e, t); if (!o) return s(); const f = o + "end"; let u = 0; const d = () => { e.removeEventListener(f, g), i() }, g = y => { y.target === e && ++u >= l && d() }; setTimeout(() => { u < l && d() }, a + 1), e.addEventListener(f, g) } function Lu(e, t) { const n = window.getComputedStyle(e), s = E => (n[E] || "").split(", "), r = s(`${Xt}Delay`), i = s(`${Xt}Duration`), o = To(r, i), a = s(`${Wn}Delay`), l = s(`${Wn}Duration`), f = To(a, l); let u = null, d = 0, g = 0; t === Xt ? o > 0 && (u = Xt, d = o, g = i.length) : t === Wn ? f > 0 && (u = Wn, d = f, g = l.length) : (d = Math.max(o, f), u = d > 0 ? o > f ? Xt : Wn : null, g = u ? u === Xt ? i.length : l.length : 0); const y = u === Xt && /\b(transform|all)(,|$)/.test(s(`${Xt}Property`).toString()); return { type: u, timeout: d, propCount: g, hasTransform: y } } function To(e, t) { for (; e.length < t.length;)e = e.concat(e); return Math.max(...t.map((n, s) => ko(n) + ko(e[s]))) } function ko(e) { return e === "auto" ? 0 : Number(e.slice(0, -1).replace(",", ".")) * 1e3 } function So() { return document.body.offsetHeight } function $u(e, t, n) { const s = e[Gn]; s && (t = (t ? [t, ...s] : [...s]).join(" ")), t == null ? e.removeAttribute("class") : n ? e.setAttribute("class", t) : e.className = t } const Eo = Symbol("_vod"), Du = Symbol("_vsh"), Nu = Symbol(""), Fu = /(^|;)\s*display\s*:/; function Bu(e, t, n) { const s = e.style, r = Pe(n); let i = !1; if (n && !r) { if (t) if (Pe(t)) for (const o of t.split(";")) { const a = o.slice(0, o.indexOf(":")).trim(); n[a] == null && Rs(s, a, "") } else for (const o in t) n[o] == null && Rs(s, o, ""); for (const o in n) o === "display" && (i = !0), Rs(s, o, n[o]) } else if (r) { if (t !== n) { const o = s[Nu]; o && (n += ";" + o), s.cssText = n, i = Fu.test(n) } } else t && e.removeAttribute("style"); Eo in e && (e[Eo] = i ? s.display : "", e[Du] && (s.display = "none")) } const Ao = /\s*!important$/; function Rs(e, t, n) { if (Q(n)) n.forEach(s => Rs(e, t, s)); else if (n == null && (n = ""), t.startsWith("--")) e.setProperty(t, n); else { const s = Hu(e, t); Ao.test(n) ? e.setProperty(en(s), n.replace(Ao, ""), "important") : e[s] = n } } const Ro = ["Webkit", "Moz", "ms"], _r = {}; function Hu(e, t) { const n = _r[t]; if (n) return n; let s = jt(t); if (s !== "filter" && s in e) return _r[t] = s; s = ri(s); for (let r = 0; r < Ro.length; r++) { const i = Ro[r] + s; if (i in e) return _r[t] = i } return t } const Co = "http://www.w3.org/1999/xlink"; function Po(e, t, n, s, r, i = Wl(t)) { s && t.startsWith("xlink:") ? n == null ? e.removeAttributeNS(Co, t.slice(6, t.length)) : e.setAttributeNS(Co, t, n) : n == null || i && !oi(n) ? e.removeAttribute(t) : e.setAttribute(t, i ? "" : Ut(n) ? String(n) : n) } function Io(e, t, n, s, r) { if (t === "innerHTML" || t === "textContent") { n != null && (e[t] = t === "innerHTML" ? bo(n) : n); return } const i = e.tagName; if (t === "value" && i !== "PROGRESS" && !i.includes("-")) { const a = i === "OPTION" ? e.getAttribute("value") || "" : e.value, l = n == null ? e.type === "checkbox" ? "on" : "" : String(n); (a !== l || !("_value" in e)) && (e.value = l), n == null && e.removeAttribute(t), e._value = n; return } let o = !1; if (n === "" || n == null) { const a = typeof e[t]; a === "boolean" ? n = oi(n) : n == null && a === "string" ? (n = "", o = !0) : a === "number" && (n = 0, o = !0) } try { e[t] = n } catch { } o && e.removeAttribute(r || t) } function vn(e, t, n, s) { e.addEventListener(t, n, s) } function zu(e, t, n, s) { e.removeEventListener(t, n, s) } const Mo = Symbol("_vei"); function Uu(e, t, n, s, r = null) { const i = e[Mo] || (e[Mo] = {}), o = i[t]; if (s && o) o.value = s; else { const [a, l] = ju(t); if (s) { const f = i[t] = Wu(s, r); vn(e, a, f, l) } else o && (zu(e, a, o, l), i[t] = void 0) } } const Oo = /(?:Once|Passive|Capture)$/; function ju(e) { let t; if (Oo.test(e)) { t = {}; let s; for (; s = e.match(Oo);)e = e.slice(0, e.length - s[0].length), t[s[0].toLowerCase()] = !0 } return [e[2] === ":" ? e.slice(3) : en(e.slice(2)), t] } let Tr = 0; const Vu = Promise.resolve(), qu = () => Tr || (Vu.then(() => Tr = 0), Tr = Date.now()); function Wu(e, t) { const n = s => { if (!s._vts) s._vts = Date.now(); else if (s._vts <= n.attached) return; Tt(Gu(s, n.value), t, 5, [s]) }; return n.value = e, n.attached = qu(), n } function Gu(e, t) { if (Q(t)) { const n = e.stopImmediatePropagation; return e.stopImmediatePropagation = () => { n.call(e), e._stopped = !0 }, t.map(s => r => !r._stopped && s && s(r)) } else return t } const Lo = e => e.charCodeAt(0) === 111 && e.charCodeAt(1) === 110 && e.charCodeAt(2) > 96 && e.charCodeAt(2) < 123, Ku = (e, t, n, s, r, i) => { const o = r === "svg"; t === "class" ? $u(e, s, o) : t === "style" ? Bu(e, n, s) : ts(t) ? Hs(t) || Uu(e, t, n, s, i) : (t[0] === "." ? (t = t.slice(1), !0) : t[0] === "^" ? (t = t.slice(1), !1) : Yu(e, t, s, o)) ? (Io(e, t, s), !e.tagName.includes("-") && (t === "value" || t === "checked" || t === "selected") && Po(e, t, s, o, i, t !== "value")) : e._isVueCE && (/[A-Z]/.test(t) || !Pe(s)) ? Io(e, jt(t), s, i, t) : (t === "true-value" ? e._trueValue = s : t === "false-value" && (e._falseValue = s), Po(e, t, s, o)) }; function Yu(e, t, n, s) { if (s) return !!(t === "innerHTML" || t === "textContent" || t in e && Lo(t) && be(n)); if (t === "spellcheck" || t === "draggable" || t === "translate" || t === "autocorrect" || t === "form" || t === "list" && e.tagName === "INPUT" || t === "type" && e.tagName === "TEXTAREA") return !1; if (t === "width" || t === "height") { const r = e.tagName; if (r === "IMG" || r === "VIDEO" || r === "CANVAS" || r === "SOURCE") return !1 } return Lo(t) && Pe(n) ? !1 : t in e } const $o = e => { const t = e.props["onUpdate:modelValue"] || !1; return Q(t) ? n => rs(t, n) : t }; function Zu(e) { e.target.composing = !0 } function Do(e) { const t = e.target; t.composing && (t.composing = !1, t.dispatchEvent(new Event("input"))) } const kr = Symbol("_assign"), Xu = { created(e, { modifiers: { lazy: t, trim: n, number: s } }, r) { e[kr] = $o(r); const i = s || r.props && r.props.type === "number"; vn(e, t ? "change" : "input", o => { if (o.target.composing) return; let a = e.value; n && (a = a.trim()), i && (a = Vs(a)), e[kr](a) }), n && vn(e, "change", () => { e.value = e.value.trim() }), t || (vn(e, "compositionstart", Zu), vn(e, "compositionend", Do), vn(e, "change", Do)) }, mounted(e, { value: t }) { e.value = t == null ? "" : t }, beforeUpdate(e, { value: t, oldValue: n, modifiers: { lazy: s, trim: r, number: i } }, o) { if (e[kr] = $o(o), e.composing) return; const a = (i || e.type === "number") && !/^0\d/.test(e.value) ? Vs(e.value) : e.value, l = t == null ? "" : t; a !== l && (document.activeElement === e && e.type !== "range" && (s && t === n || r && e.value.trim() === l) || (e.value = l)) } }, Qu = ["ctrl", "shift", "alt", "meta"], Ju = { stop: e => e.stopPropagation(), prevent: e => e.preventDefault(), self: e => e.target !== e.currentTarget, ctrl: e => !e.ctrlKey, shift: e => !e.shiftKey, alt: e => !e.altKey, meta: e => !e.metaKey, left: e => "button" in e && e.button !== 0, middle: e => "button" in e && e.button !== 1, right: e => "button" in e && e.button !== 2, exact: (e, t) => Qu.some(n => e[`${n}Key`] && !t.includes(n)) }, pt = (e, t) => { const n = e._withMods || (e._withMods = {}), s = t.join("."); return n[s] || (n[s] = (r, ...i) => { for (let o = 0; o < t.length; o++) { const a = Ju[t[o]]; if (a && a(r, t)) return } return e(r, ...i) }) }, ec = bt({ patchProp: Ku }, Ru); let No; function tc() { return No || (No = Ka(ec)) } const nc = (...e) => { const t = tc().createApp(...e), { mount: n } = t; return t.mount = s => { const r = rc(s); if (!r) return; const i = t._component; !be(i) && !i.render && !i.template && (i.template = r.innerHTML), r.nodeType === 1 && (r.textContent = ""); const o = n(r, !1, sc(r)); return r instanceof Element && (r.removeAttribute("v-cloak"), r.setAttribute("data-v-app", "")), o }, t }; function sc(e) { if (e instanceof SVGElement) return "svg"; if (typeof MathMLElement == "function" && e instanceof MathMLElement) return "mathml" } function rc(e) { return Pe(e) ? document.querySelector(e) : e } const ic = ["aria-pressed"], oc = { key: 0, viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true" }, lc = { key: 1, viewBox: "0 0 24 24", width: "18", height: "18", "aria-hidden": "true", fill: "none" }, ac = { __name: "ChatbotToggler", props: { isOpen: { type: Boolean, required: !0 } }, emits: ["toggle"], setup(e) { return (t, n) => (F(), W("button", { class: "fixed bottom-5 right-5 z-9999 grid h-12 w-12 appearance-none place-items-center rounded-full border border-white/20 bg-gradient-to-br from-brand-500 to-brand-600 text-white shadow-[0_20px_36px_-20px_rgba(109,79,194,0.85)] transition-all duration-250 hover:-translate-y-0.5 hover:from-brand-600 hover:to-violet-700 hover:shadow-[0_22px_40px_-22px_rgba(109,79,194,1)] focus:outline-none max-[600px]:bottom-3 max-[600px]:right-3 max-[600px]:h-13 max-[600px]:w-13", style: { "border-radius": "9999px" }, "aria-pressed": e.isOpen ? "true" : "false", onClick: n[0] || (n[0] = s => t.$emit("toggle")) }, [e.isOpen ? (F(), W("svg", lc, n[2] || (n[2] = [w("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }, null, -1)]))) : (F(), W("svg", oc, n[1] || (n[1] = [w("path", { d: "M4 4h16a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H8l-4 4v-4H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z", fill: "currentColor", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "miter", "stroke-linejoin": "miter" }, null, -1)])))], 8, ic)) } }, uc = { class: "chat-header relative flex min-h-14 items-center justify-between px-4 pb-2.5 pt-3 text-white sm:px-5" }, cc = { class: "flex min-w-0 flex-1 items-center gap-2 sm:gap-2.5" }, fc = { xmlns: "http://www.w3.org/2000/svg", width: "35", height: "35", viewBox: "0 0 1024 1024", class: "h-8 w-8 shrink-0 rounded-full bg-white p-1.5 shadow-md motion-safe:animate-soft-float", style: { fill: "#6d4fc2" } }, pc = { class: "ml-2 flex items-center gap-1.5" }, dc = ["title"], hc = ["title", "aria-label"], gc = { key: 0, viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, mc = { key: 1, viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, bc = ["title", "aria-label"], xc = { key: 0, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, yc = { key: 1, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, wc = { key: 2, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, vc = { __name: "ChatHeader", props: { windowMode: { type: String, required: !0 }, autoReadEnabled: { type: Boolean, required: !0 }, activeTtsProvider: { type: String, required: !0 } }, emits: ["close", "cycleResize", "toggleAutoRead"], setup(e) { const t = e, n = { default: "Compact", half: "Half Screen", full: "Full Screen" }, s = Ee(() => n[t.windowMode] || "Compact"), r = Ee(() => t.windowMode === "default" ? "Half Screen" : t.windowMode === "half" ? "Full Screen" : "Compact"), i = Ee(() => t.activeTtsProvider === "polly" ? "TTS: Polly" : t.activeTtsProvider === "browser" ? "TTS: Browser" : "TTS: Off"), o = Ee(() => t.activeTtsProvider === "polly" ? "bg-emerald-500/45" : t.activeTtsProvider === "browser" ? "bg-amber-500/45" : "bg-slate-500/35"); return (a, l) => (F(), W("div", uc, [w("div", cc, [(F(), W("svg", fc, l[3] || (l[3] = [w("path", { d: "M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z" }, null, -1)]))), l[4] || (l[4] = w("h2", { class: "truncate text-xs font-semibold tracking-[0.01em] sm:text-base text-white/95" }, "ChangAI from ERPGulf", -1))]), w("div", pc, [w("span", { class: we(["hidden rounded-full border border-white/25 px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-white/95 shadow-sm backdrop-blur-sm sm:inline", o.value]), title: `TTS provider: ${i.value}` }, Je(i.value), 11, dc), w("button", { class: we(["h-8 min-w-8 appearance-none items-center justify-center rounded-md border border-white/20 px-2 text-xs font-semibold text-white/90 transition-all duration-200 focus:outline-none sm:flex", e.autoReadEnabled ? "bg-white/24 shadow-sm" : "hover:bg-white/15"]), style: { "border-radius": "0.375rem" }, title: e.autoReadEnabled ? "Auto speech on" : "Auto speech off", "aria-label": e.autoReadEnabled ? "Turn off auto speech" : "Turn on auto speech", onClick: l[0] || (l[0] = f => a.$emit("toggleAutoRead")) }, [e.autoReadEnabled ? (F(), W("svg", gc, l[5] || (l[5] = [w("path", { d: "M11 5L6 9H3v6h3l5 4V5z" }, null, -1), w("path", { d: "M15 9a4 4 0 0 1 0 6" }, null, -1), w("path", { d: "M18 7a7 7 0 0 1 0 10" }, null, -1)]))) : (F(), W("svg", mc, l[6] || (l[6] = [w("path", { d: "M11 5L6 9H3v6h3l5 4V5z" }, null, -1), w("path", { d: "M22 9l-6 6" }, null, -1), w("path", { d: "M16 9l6 6" }, null, -1)])))], 10, hc), w("button", { class: we(["flex h-8 min-w-8 appearance-none items-center justify-center rounded-md border border-white/20 px-2 text-xs font-semibold text-white/90 transition-all duration-200 focus:outline-none", "bg-white/20 shadow-sm hover:bg-white/25"]), style: { "border-radius": "0.375rem" }, title: `Resize mode: ${s.value} (click to ${r.value})`, "aria-label": `Resize mode ${s.value}. Click to switch to ${r.value}`, onClick: l[1] || (l[1] = f => a.$emit("cycleResize")) }, [e.windowMode === "default" ? (F(), W("svg", xc, l[7] || (l[7] = [w("rect", { x: "7", y: "8", width: "10", height: "8", rx: "2" }, null, -1)]))) : e.windowMode === "half" ? (F(), W("svg", yc, l[8] || (l[8] = [w("rect", { x: "4", y: "5", width: "16", height: "14", rx: "2" }, null, -1), w("path", { d: "M12 5v14" }, null, -1)]))) : (F(), W("svg", wc, l[9] || (l[9] = [w("rect", { x: "4", y: "5", width: "16", height: "14", rx: "2" }, null, -1), w("path", { d: "M8 8H6v2M16 8h2v2M8 16H6v-2M16 16h2v-2" }, null, -1)])))], 8, bc), w("button", { class: "grid h-8 w-8 shrink-0 appearance-none place-items-center rounded-full border border-white/20 text-white transition-all duration-200 hover:scale-105 hover:bg-white/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/70", style: { "border-radius": "9999px" }, "aria-label": "Close chatbot", onClick: l[2] || (l[2] = f => a.$emit("close")) }, l[10] || (l[10] = [w("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24", width: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, [w("path", { d: "M6 9l6 6 6-6" })], -1)]))])])) } }, _c = { class: "flex gap-1.5 border-b border-slate-200/80 px-2.5 pb-2.5 pt-1" }, Tc = ["onClick"], kc = { class: "inline-flex items-center gap-1.5" }, Sc = { __name: "TabBar", props: { modelValue: { type: String, required: !0 }, debugEnabled: { type: Boolean, default: !1 } }, emits: ["update:modelValue"], setup(e) { const t = e, n = Ee(() => { const s = [{ id: "chat", label: "Chats" }, { id: "debug", label: "Debug" }, { id: "support", label: "Support" }, { id: "settings", label: "Settings" }]; return t.debugEnabled ? s : s.filter(r => r.id !== "debug") }); return (s, r) => (F(), W("div", _c, [(F(!0), W(He, null, ws(n.value, i => (F(), W("button", { key: i.id, class: we(["group min-w-0 flex-1 h-9 appearance-none rounded-lg border border-transparent bg-transparent px-2 text-xs font-semibold transition-all duration-200 focus:outline-none", e.modelValue === i.id ? "border-white/30 bg-linear-to-r from-violet-300/36 via-indigo-300/30 to-sky-300/28 text-white shadow-[0_4px_10px_rgba(20,24,40,0.22)]" : "text-white/80 hover:border-white/25 hover:bg-white/12 hover:text-white"]), onClick: o => s.$emit("update:modelValue", i.id) }, [w("span", kc, [w("span", { class: we(["h-1.5 w-1.5 rounded-full transition-colors duration-200", e.modelValue === i.id ? "bg-white" : "bg-white/40 group-hover:bg-white/70"]) }, null, 2), Ss(" " + Je(i.label), 1)])], 10, Tc))), 128))])) } }, Ec = (e, t) => { const n = e.__vccOpts || e; for (const [s, r] of t) n[s] = r; return n }, Ac = {}, Rc = { xmlns: "http://www.w3.org/2000/svg", width: "50", height: "50", viewBox: "0 0 1024 1024", class: "h-7.5 w-7.5 shrink-0 self-end rounded-full bg-gradient-to-br from-brand-500 to-brand-600 p-1.5 fill-white shadow-[0_10px_18px_-12px_rgba(109,79,194,0.85)]" }; function Cc(e, t) { return F(), W("svg", Rc, t[0] || (t[0] = [w("path", { d: "M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z" }, null, -1)])) } const Fo = Ec(Ac, [["render", Cc]]), Cs = { PIPELINE: "changai.changai.api.v2.text2sql_pipeline_v2.run_text2sql_pipeline", SUPPORT: "changai.changai.api.v2.helpdesk_api.support_bot", SETTINGS: "changai.changai.api.v2.schema_utils.get_frontend_settings", TTS: "changai.changai.api.v2.tts.synthesize_tts" }; function Bo(e, t = {}, n = "actual") { return n === "test" ? Promise.resolve({ Bot: `[TEST MODE] ${JSON.stringify(t)}` }) : !window.frappe || !window.frappe.call ? Promise.reject(new Error("Frappe API is unavailable in actual mode.")) : new Promise((s, r) => { window.frappe.call({ method: e, args: t, callback(i) { s(i.message) }, error(i) { r(i) } }) }) } function Pc(e, t, n = "actual", s = null, r) { if (n === "test") return { promise: Promise.resolve({ Bot: `[TEST MODE] ${JSON.stringify({ user_question: e, chat_id: t, request_id: s, sendNonErptoAI: r })}` }), cancel: () => !1 }; if (!window.frappe || !window.frappe.call) return { promise: Promise.reject(new Error("Frappe API is unavailable in actual mode.")), cancel: () => !1 }; let i = null, o = !1; return { promise: new Promise((f, u) => { i = window.frappe.call({ method: Cs.PIPELINE, args: { user_question: e, chat_id: t, request_id: s, sendNonErptoAI: r }, callback(d) { o = !0, f(d.message) }, error(d) { o = !0, u(d) } }) }), cancel: () => o || !i || typeof i.abort != "function" ? !1 : (i.abort(), o = !0, !0) } } function Ic(e, t = "actual") { if (t === "test") return { promise: Promise.resolve(`[TEST MODE] ${JSON.stringify({ message: e })}`), cancel: () => !1 }; if (!window.frappe || !window.frappe.call) return { promise: Promise.reject(new Error("Frappe API is unavailable in actual mode.")), cancel: () => !1 }; let n = null, s = !1; return { promise: new Promise((o, a) => { n = window.frappe.call({ method: Cs.SUPPORT, args: { message: e }, callback(l) { s = !0, o(l.message) }, error(l) { s = !0, a(l) } }) }), cancel: () => s || !n || typeof n.abort != "function" ? !1 : (n.abort(), s = !0, !0) } } function Mc(e = "actual") { return Bo(Cs.SETTINGS, {}, e) } function Oc(e, t = "Zayd", n = "actual") { return Bo(Cs.TTS, { text: e, voice_id: t }, n) } function Sr() { return { async: !1, breaks: !1, extensions: null, gfm: !0, hooks: null, pedantic: !1, renderer: null, silent: !1, tokenizer: null, walkTokens: null } } var cn = Sr(); function Ho(e) { cn = e } var fn = { exec: () => null }; function le(e, t = "") { let n = typeof e == "string" ? e : e.source, s = { replace: (r, i) => { let o = typeof i == "string" ? i : i.source; return o = o.replace(We.caret, "$1"), n = n.replace(r, o), s }, getRegex: () => new RegExp(n, t) }; return s } var Lc = ((e = "") => { try { return !!new RegExp("(?<=1)(?/, blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g, blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm, listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g, listIsTask: /^\[[ xX]\] +\S/, listReplaceTask: /^\[[ xX]\] +/, listTaskCheckbox: /\[[ xX]\]/, anyLine: /\n.*\n/, hrefBrackets: /^<(.*)>$/, tableDelimiter: /[:|]/, tableAlignChars: /^\||\| *$/g, tableRowBlankLine: /\n[ \t]*$/, tableAlignRight: /^ *-+: *$/, tableAlignCenter: /^ *:-+: *$/, tableAlignLeft: /^ *:-+ *$/, startATag: /^/i, startPreScriptTag: /^<(pre|code|kbd|script)(\s|>)/i, endPreScriptTag: /^<\/(pre|code|kbd|script)(\s|>)/i, startAngleBracket: /^$/, pedanticHrefTitle: /^([^'"]*[^\s])\s+(['"])(.*)\2/, unicodeAlphaNumeric: /[\p{L}\p{N}]/u, escapeTest: /[&<>"']/, escapeReplace: /[&<>"']/g, escapeTestNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/, escapeReplaceNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g, caret: /(^|[^\[])\^/g, percentDecode: /%25/g, findPipe: /\|/g, splitPipe: / \|/, slashPipe: /\\\|/g, carriageReturn: /\r\n|\r/g, spaceLine: /^ +$/gm, notSpaceStart: /^\S*/, endingNewline: /\n$/, listItemRegex: e => new RegExp(`^( {0,3}${e})((?:[ ][^\\n]*)?(?:\\n|$))`), nextBulletRegex: e => new RegExp(`^ {0,${Math.min(3, e - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`), hrRegex: e => new RegExp(`^ {0,${Math.min(3, e - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`), fencesBeginRegex: e => new RegExp(`^ {0,${Math.min(3, e - 1)}}(?:\`\`\`|~~~)`), headingBeginRegex: e => new RegExp(`^ {0,${Math.min(3, e - 1)}}#`), htmlBeginRegex: e => new RegExp(`^ {0,${Math.min(3, e - 1)}}<(?:[a-z].*>|!--)`, "i"), blockquoteBeginRegex: e => new RegExp(`^ {0,${Math.min(3, e - 1)}}>`) }, $c = /^(?:[ \t]*(?:\n|$))+/, Dc = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/, Nc = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/, Kn = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/, Fc = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, Er = / {0,3}(?:[*+-]|\d{1,9}[.)])/, zo = /^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/, Uo = le(zo).replace(/bull/g, Er).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}/).replace(/html/g, / {0,3}<[^\n>]+>\n/).replace(/\|table/g, "").getRegex(), Bc = le(zo).replace(/bull/g, Er).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}/).replace(/html/g, / {0,3}<[^\n>]+>\n/).replace(/table/g, / {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(), Ar = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/, Hc = /^[^\n]+/, Rr = /(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/, zc = le(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label", Rr).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(), Uc = le(/^(bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g, Er).getRegex(), Ps = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul", Cr = /|$))/, jc = le("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))", "i").replace("comment", Cr).replace("tag", Ps).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(), jo = le(Ar).replace("hr", Kn).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", ")|<(?:script|pre|style|textarea|!--)").replace("tag", Ps).getRegex(), Vc = le(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", jo).getRegex(), Pr = { blockquote: Vc, code: Dc, def: zc, fences: Nc, heading: Fc, hr: Kn, html: jc, lheading: Uo, list: Uc, newline: $c, paragraph: jo, table: fn, text: Hc }, Vo = le("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", Kn).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", "(?: {4}| {0,3} )[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", ")|<(?:script|pre|style|textarea|!--)").replace("tag", Ps).getRegex(), qc = { ...Pr, lheading: Bc, table: Vo, paragraph: le(Ar).replace("hr", Kn).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", Vo).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", ")|<(?:script|pre|style|textarea|!--)").replace("tag", Ps).getRegex() }, Wc = { + ...Pr, html: le(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", Cr).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(), def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, fences: fn, lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/, paragraph: le(Ar).replace("hr", Kn).replace("heading", ` *#{1,6} *[^ +]`).replace("lheading", Uo).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex() + }, Gc = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, Kc = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, qo = /^( {2,}|\\)\n(?!\s*$)/, Yc = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-", Lc ? "(?`+)[^`]+\k(?!`)/).replace("html", /<(?! )[^<>]*?>/).getRegex(), Go = /^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/, ef = le(Go, "u").replace(/punct/g, _n).getRegex(), tf = le(Go, "u").replace(/punct/g, Wo).getRegex(), Ko = "^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)", nf = le(Ko, "gu").replace(/notPunctSpace/g, Ir).replace(/punctSpace/g, Is).replace(/punct/g, _n).getRegex(), sf = le(Ko, "gu").replace(/notPunctSpace/g, Qc).replace(/punctSpace/g, Xc).replace(/punct/g, Wo).getRegex(), rf = le("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)", "gu").replace(/notPunctSpace/g, Ir).replace(/punctSpace/g, Is).replace(/punct/g, _n).getRegex(), of = le(/^~~?(?:((?!~)punct)|[^\s~])/, "u").replace(/punct/g, _n).getRegex(), lf = "^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)", af = le(lf, "gu").replace(/notPunctSpace/g, Ir).replace(/punctSpace/g, Is).replace(/punct/g, _n).getRegex(), uf = le(/\\(punct)/, "gu").replace(/punct/g, _n).getRegex(), cf = le(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(), ff = le(Cr).replace("(?:-->|$)", "-->").getRegex(), pf = le("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment", ff).replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(), Ms = /(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/, df = le(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label", Ms).replace("href", /<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(), Yo = le(/^!?\[(label)\]\[(ref)\]/).replace("label", Ms).replace("ref", Rr).getRegex(), Zo = le(/^!?\[(ref)\](?:\[\])?/).replace("ref", Rr).getRegex(), hf = le("reflink|nolink(?!\\()", "g").replace("reflink", Yo).replace("nolink", Zo).getRegex(), Xo = /[hH][tT][tT][pP][sS]?|[fF][tT][pP]/, Mr = { _backpedal: fn, anyPunctuation: uf, autolink: cf, blockSkip: Jc, br: qo, code: Kc, del: fn, delLDelim: fn, delRDelim: fn, emStrongLDelim: ef, emStrongRDelimAst: nf, emStrongRDelimUnd: rf, escape: Gc, link: df, nolink: Zo, punctuation: Zc, reflink: Yo, reflinkSearch: hf, tag: pf, text: Yc, url: fn }, gf = { ...Mr, link: le(/^!?\[(label)\]\((.*?)\)/).replace("label", Ms).getRegex(), reflink: le(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label", Ms).getRegex() }, Or = { ...Mr, emStrongRDelimAst: sf, emStrongLDelim: tf, delLDelim: of, delRDelim: af, url: le(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol", Xo).replace("email", /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(), _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/, del: /^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/, text: le(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\": ">", '"': """, "'": "'" }, Qo = e => bf[e]; function Ct(e, t) { if (t) { if (We.escapeTest.test(e)) return e.replace(We.escapeReplace, Qo) } else if (We.escapeTestNoEncode.test(e)) return e.replace(We.escapeReplaceNoEncode, Qo); return e } function Jo(e) { try { e = encodeURI(e).replace(We.percentDecode, "%") } catch { return null } return e } function el(e, t) { var i; let n = e.replace(We.findPipe, (o, a, l) => { let f = !1, u = a; for (; --u >= 0 && l[u] === "\\";)f = !f; return f ? "|" : " |" }), s = n.split(We.splitPipe), r = 0; if (s[0].trim() || s.shift(), s.length > 0 && !((i = s.at(-1)) != null && i.trim()) && s.pop(), t) if (s.length > t) s.splice(t); else for (; s.length < t;)s.push(""); for (; r < s.length; r++)s[r] = s[r].trim().replace(We.slashPipe, "|"); return s } function Qt(e, t, n) { let s = e.length; if (s === 0) return ""; let r = 0; for (; r < s && e.charAt(s - r - 1) === t;)r++; return e.slice(0, s - r) } function tl(e) { + let t = e.split(` +`), n = t.length - 1; for (; n >= 0 && We.blankLine.test(t[n]);)n--; return t.length - n <= 2 ? e : t.slice(0, n + 1).join(` +`) + } function xf(e, t) { if (e.indexOf(t[1]) === -1) return -1; let n = 0; for (let s = 0; s < e.length; s++)if (e[s] === "\\") s++; else if (e[s] === t[0]) n++; else if (e[s] === t[1] && (n--, n < 0)) return s; return n > 0 ? -2 : -1 } function yf(e, t = 0) { let n = t, s = ""; for (let r of e) if (r === " ") { let i = 4 - n % 4; s += " ".repeat(i), n += i } else s += r, n++; return s } function nl(e, t, n, s, r) { let i = t.href, o = t.title || null, a = e[1].replace(r.other.outputLinkReplace, "$1"); s.state.inLink = !0; let l = { type: e[0].charAt(0) === "!" ? "image" : "link", raw: n, href: i, title: o, text: a, tokens: s.inlineTokens(a) }; return s.state.inLink = !1, l } function wf(e, t, n) { + let s = e.match(n.other.indentCodeCompensation); if (s === null) return t; let r = s[1]; return t.split(` +`).map(i => { let o = i.match(n.other.beginningSpace); if (o === null) return i; let [a] = o; return a.length >= r.length ? i.slice(r.length) : i }).join(` +`) + } var Ls = class { + constructor(e) { _e(this, "options"); _e(this, "rules"); _e(this, "lexer"); this.options = e || cn } space(e) { let t = this.rules.block.newline.exec(e); if (t && t[0].length > 0) return { type: "space", raw: t[0] } } code(e) { let t = this.rules.block.code.exec(e); if (t) { let n = this.options.pedantic ? t[0] : tl(t[0]), s = n.replace(this.rules.other.codeRemoveIndent, ""); return { type: "code", raw: n, codeBlockStyle: "indented", text: s } } } fences(e) { let t = this.rules.block.fences.exec(e); if (t) { let n = t[0], s = wf(n, t[3] || "", this.rules); return { type: "code", raw: n, lang: t[2] ? t[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : t[2], text: s } } } heading(e) { + let t = this.rules.block.heading.exec(e); if (t) { + let n = t[2].trim(); if (this.rules.other.endingHash.test(n)) { let s = Qt(n, "#"); (this.options.pedantic || !s || this.rules.other.endingSpaceChar.test(s)) && (n = s.trim()) } return { + type: "heading", raw: Qt(t[0], ` +`), depth: t[1].length, text: n, tokens: this.lexer.inline(n) + } + } + } hr(e) { + let t = this.rules.block.hr.exec(e); if (t) return { + type: "hr", raw: Qt(t[0], ` +`) + } + } blockquote(e) { + let t = this.rules.block.blockquote.exec(e); if (t) { + let n = Qt(t[0], ` `).split(` -`),s="",r="",i=[];for(;n.length>0;){let o=!1,a=[],l;for(l=0;l1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let i=this.rules.other.listItemRegex(n),o=!1;for(;e;){let l=!1,f="",u="";if(!(t=i.exec(e))||this.rules.block.hr.test(e))break;f=t[0],e=e.substring(f.length);let d=yf(t[2].split(` -`,1)[0],t[1].length),g=e.split(` -`,1)[0],y=!d.trim(),E=0;if(this.options.pedantic?(E=2,u=d.trimStart()):y?E=t[1].length+1:(E=d.search(this.rules.other.nonSpaceChar),E=E>4?1:E,u=d.slice(E),E+=t[1].length),y&&this.rules.other.blankLine.test(g)&&(f+=g+` -`,e=e.substring(g.length+1),l=!0),!l){let x=this.rules.other.nextBulletRegex(E),M=this.rules.other.hrRegex(E),m=this.rules.other.fencesBeginRegex(E),H=this.rules.other.headingBeginRegex(E),Y=this.rules.other.htmlBeginRegex(E),$=this.rules.other.blockquoteBeginRegex(E);for(;e;){let z=e.split(` -`,1)[0],O;if(g=z,this.options.pedantic?(g=g.replace(this.rules.other.listReplaceNesting," "),O=g):O=g.replace(this.rules.other.tabCharGlobal," "),m.test(g)||H.test(g)||Y.test(g)||$.test(g)||x.test(g)||M.test(g))break;if(O.search(this.rules.other.nonSpaceChar)>=E||!g.trim())u+=` -`+O.slice(E);else{if(y||d.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||m.test(d)||H.test(d)||M.test(d))break;u+=` -`+g}y=!g.trim(),f+=z+` -`,e=e.substring(z.length+1),d=O.slice(E)}}r.loose||(o?r.loose=!0:this.rules.other.doubleBlankLine.test(f)&&(o=!0)),r.items.push({type:"list_item",raw:f,task:!!this.options.gfm&&this.rules.other.listIsTask.test(u),loose:!1,text:u,tokens:[]}),r.raw+=f}let a=r.items.at(-1);if(a)a.raw=a.raw.trimEnd(),a.text=a.text.trimEnd();else return;r.raw=r.raw.trimEnd();for(let l of r.items){this.lexer.state.top=!1,l.tokens=this.lexer.blockTokens(l.text,[]);let f=l.tokens[0];if(l.task&&((f==null?void 0:f.type)==="text"||(f==null?void 0:f.type)==="paragraph")){l.text=l.text.replace(this.rules.other.listReplaceTask,""),f.raw=f.raw.replace(this.rules.other.listReplaceTask,""),f.text=f.text.replace(this.rules.other.listReplaceTask,"");for(let d=this.lexer.inlineQueue.length-1;d>=0;d--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[d].src)){this.lexer.inlineQueue[d].src=this.lexer.inlineQueue[d].src.replace(this.rules.other.listReplaceTask,"");break}let u=this.rules.other.listTaskCheckbox.exec(l.raw);if(u){let d={type:"checkbox",raw:u[0]+" ",checked:u[0]!=="[ ]"};l.checked=d.checked,r.loose?l.tokens[0]&&["paragraph","text"].includes(l.tokens[0].type)&&"tokens"in l.tokens[0]&&l.tokens[0].tokens?(l.tokens[0].raw=d.raw+l.tokens[0].raw,l.tokens[0].text=d.raw+l.tokens[0].text,l.tokens[0].tokens.unshift(d)):l.tokens.unshift({type:"paragraph",raw:d.raw,text:d.raw,tokens:[d]}):l.tokens.unshift(d)}}else l.task&&(l.task=!1);if(!r.loose){let u=l.tokens.filter(g=>g.type==="space"),d=u.length>0&&u.some(g=>this.rules.other.anyLine.test(g.raw));r.loose=d}}if(r.loose)for(let l of r.items){l.loose=!0;for(let f of l.tokens)f.type==="text"&&(f.type="paragraph")}return r}}html(e){let t=this.rules.block.html.exec(e);if(t){let n=tl(t[0]);return{type:"html",block:!0,raw:n,pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:n}}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",r=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:Qt(t[0],` -`),href:s,title:r}}}table(e){var o;let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=el(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),r=(o=t[3])!=null&&o.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(` -`):[],i={type:"table",raw:Qt(t[0],` -`),header:[],align:[],rows:[]};if(n.length===s.length){for(let a of s)this.rules.other.tableAlignRight.test(a)?i.align.push("right"):this.rules.other.tableAlignCenter.test(a)?i.align.push("center"):this.rules.other.tableAlignLeft.test(a)?i.align.push("left"):i.align.push(null);for(let a=0;a({text:l,tokens:this.lexer.inline(l),header:!1,align:i.align[f]})));return i}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t){let n=t[1].trim();return{type:"heading",raw:Qt(t[0],` -`),depth:t[2].charAt(0)==="="?1:2,text:n,tokens:this.lexer.inline(n)}}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===` -`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let i=Qt(n.slice(0,-1),"\\");if((n.length-i.length)%2===0)return}else{let i=xf(t[2],"()");if(i===-2)return;if(i>-1){let o=(t[0].indexOf("!")===0?5:4)+t[1].length+i;t[2]=t[2].substring(0,i),t[0]=t[0].substring(0,o).trim(),t[3]=""}}let s=t[2],r="";if(this.options.pedantic){let i=this.rules.other.pedanticHrefTitle.exec(s);i&&(s=i[1],r=i[3])}else r=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),nl(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:r&&r.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),r=t[s.toLowerCase()];if(!r){let i=n[0].charAt(0);return{type:"text",raw:i,text:i}}return nl(n,r,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!(!s||!s[1]&&!s[2]&&!s[3]&&!s[4]||s[4]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(s[1]||s[3])||!n||this.rules.inline.punctuation.exec(n))){let r=[...s[0]].length-1,i,o,a=r,l=0,f=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(f.lastIndex=0,t=t.slice(-1*e.length+r);(s=f.exec(t))!==null;){if(i=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!i)continue;if(o=[...i].length,s[3]||s[4]){a+=o;continue}else if((s[5]||s[6])&&r%3&&!((r+o)%3)){l+=o;continue}if(a-=o,a>0)continue;o=Math.min(o,o+a+l);let u=[...s[0]][0].length,d=e.slice(0,r+s.index+u+o);if(Math.min(r,o)%2){let y=d.slice(1,-1);return{type:"em",raw:d,text:y,tokens:this.lexer.inlineTokens(y)}}let g=d.slice(2,-2);return{type:"strong",raw:d,text:g,tokens:this.lexer.inlineTokens(g)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),r=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&r&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e,t,n=""){let s=this.rules.inline.delLDelim.exec(e);if(s&&(!s[1]||!n||this.rules.inline.punctuation.exec(n))){let r=[...s[0]].length-1,i,o,a=r,l=this.rules.inline.delRDelim;for(l.lastIndex=0,t=t.slice(-1*e.length+r);(s=l.exec(t))!==null;){if(i=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!i||(o=[...i].length,o!==r))continue;if(s[3]||s[4]){a+=o;continue}if(a-=o,a>0)continue;o=Math.min(o,o+a);let f=[...s[0]][0].length,u=e.slice(0,r+s.index+f+o),d=u.slice(r,-r);return{type:"del",raw:u,text:d,tokens:this.lexer.inlineTokens(d)}}}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){var n,s;let t;if(t=this.rules.inline.url.exec(e)){let r,i;if(t[2]==="@")r=t[0],i="mailto:"+r;else{let o;do o=t[0],t[0]=(s=(n=this.rules.inline._backpedal.exec(t[0]))==null?void 0:n[0])!=null?s:"";while(o!==t[0]);r=t[0],t[1]==="www."?i="http://"+t[0]:i=t[0]}return{type:"link",raw:t[0],text:r,href:i,tokens:[{type:"text",raw:r,text:r}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}},yt=class Xr{constructor(t){_e(this,"tokens");_e(this,"options");_e(this,"state");_e(this,"inlineQueue");_e(this,"tokenizer");this.tokens=[],this.tokens.links=Object.create(null),this.options=t||cn,this.options.tokenizer=this.options.tokenizer||new Ls,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:We,block:Os.normal,inline:Yn.normal};this.options.pedantic?(n.block=Os.pedantic,n.inline=Yn.pedantic):this.options.gfm&&(n.block=Os.gfm,this.options.breaks?n.inline=Yn.breaks:n.inline=Yn.gfm),this.tokenizer.rules=n}static get rules(){return{block:Os,inline:Yn}}static lex(t,n){return new Xr(n).lex(t)}static lexInline(t,n){return new Xr(n).inlineTokens(t)}lex(t){t=t.replace(We.carriageReturn,` -`),this.blockTokens(t,this.tokens);for(let n=0;n(l=u.call({lexer:this},t,n))?(t=t.substring(l.raw.length),n.push(l),!0):!1))continue;if(l=this.tokenizer.space(t)){t=t.substring(l.raw.length);let u=n.at(-1);l.raw.length===1&&u!==void 0?u.raw+=` -`:n.push(l);continue}if(l=this.tokenizer.code(t)){t=t.substring(l.raw.length);let u=n.at(-1);(u==null?void 0:u.type)==="paragraph"||(u==null?void 0:u.type)==="text"?(u.raw+=(u.raw.endsWith(` -`)?"":` -`)+l.raw,u.text+=` -`+l.text,this.inlineQueue.at(-1).src=u.text):n.push(l);continue}if(l=this.tokenizer.fences(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.heading(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.hr(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.blockquote(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.list(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.html(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.def(t)){t=t.substring(l.raw.length);let u=n.at(-1);(u==null?void 0:u.type)==="paragraph"||(u==null?void 0:u.type)==="text"?(u.raw+=(u.raw.endsWith(` -`)?"":` -`)+l.raw,u.text+=` -`+l.raw,this.inlineQueue.at(-1).src=u.text):this.tokens.links[l.tag]||(this.tokens.links[l.tag]={href:l.href,title:l.title},n.push(l));continue}if(l=this.tokenizer.table(t)){t=t.substring(l.raw.length),n.push(l);continue}if(l=this.tokenizer.lheading(t)){t=t.substring(l.raw.length),n.push(l);continue}let f=t;if((a=this.options.extensions)!=null&&a.startBlock){let u=1/0,d=t.slice(1),g;this.options.extensions.startBlock.forEach(y=>{g=y.call({lexer:this},d),typeof g=="number"&&g>=0&&(u=Math.min(u,g))}),u<1/0&&u>=0&&(f=t.substring(0,u+1))}if(this.state.top&&(l=this.tokenizer.paragraph(f))){let u=n.at(-1);s&&(u==null?void 0:u.type)==="paragraph"?(u.raw+=(u.raw.endsWith(` -`)?"":` -`)+l.raw,u.text+=` -`+l.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=u.text):n.push(l),s=f.length!==t.length,t=t.substring(l.raw.length);continue}if(l=this.tokenizer.text(t)){t=t.substring(l.raw.length);let u=n.at(-1);(u==null?void 0:u.type)==="text"?(u.raw+=(u.raw.endsWith(` -`)?"":` -`)+l.raw,u.text+=` -`+l.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=u.text):n.push(l);continue}if(t){this.infiniteLoopError(t.charCodeAt(0));break}}return this.state.top=!0,n}inline(t,n=[]){return this.inlineQueue.push({src:t,tokens:n}),n}inlineTokens(t,n=[]){var f,u,d,g,y,E;this.tokenizer.lexer=this;let s=t,r=null;if(this.tokens.links){let x=Object.keys(this.tokens.links);if(x.length>0)for(;(r=this.tokenizer.rules.inline.reflinkSearch.exec(s))!==null;)x.includes(r[0].slice(r[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,r.index)+"["+"a".repeat(r[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(r=this.tokenizer.rules.inline.anyPunctuation.exec(s))!==null;)s=s.slice(0,r.index)+"++"+s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let i;for(;(r=this.tokenizer.rules.inline.blockSkip.exec(s))!==null;)i=r[2]?r[2].length:0,s=s.slice(0,r.index+i)+"["+"a".repeat(r[0].length-i-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);s=(d=(u=(f=this.options.hooks)==null?void 0:f.emStrongMask)==null?void 0:u.call({lexer:this},s))!=null?d:s;let o=!1,a="",l=1/0;for(;t;){if(t.length(x=m.call({lexer:this},t,n))?(t=t.substring(x.raw.length),n.push(x),!0):!1))continue;if(x=this.tokenizer.escape(t)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.tag(t)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.link(t)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.reflink(t,this.tokens.links)){t=t.substring(x.raw.length);let m=n.at(-1);x.type==="text"&&(m==null?void 0:m.type)==="text"?(m.raw+=x.raw,m.text+=x.text):n.push(x);continue}if(x=this.tokenizer.emStrong(t,s,a)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.codespan(t)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.br(t)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.del(t,s,a)){t=t.substring(x.raw.length),n.push(x);continue}if(x=this.tokenizer.autolink(t)){t=t.substring(x.raw.length),n.push(x);continue}if(!this.state.inLink&&(x=this.tokenizer.url(t))){t=t.substring(x.raw.length),n.push(x);continue}let M=t;if((E=this.options.extensions)!=null&&E.startInline){let m=1/0,H=t.slice(1),Y;this.options.extensions.startInline.forEach($=>{Y=$.call({lexer:this},H),typeof Y=="number"&&Y>=0&&(m=Math.min(m,Y))}),m<1/0&&m>=0&&(M=t.substring(0,m+1))}if(x=this.tokenizer.inlineText(M)){t=t.substring(x.raw.length),x.raw.slice(-1)!=="_"&&(a=x.raw.slice(-1)),o=!0;let m=n.at(-1);(m==null?void 0:m.type)==="text"?(m.raw+=x.raw,m.text+=x.text):n.push(x);continue}if(t){this.infiniteLoopError(t.charCodeAt(0));break}}return n}infiniteLoopError(t){let n="Infinite loop on byte: "+t;if(!this.options.silent)throw new Error(n)}},$s=class{constructor(e){_e(this,"options");_e(this,"parser");this.options=e||cn}space(e){return""}code({text:e,lang:t,escaped:n}){var i;let s=(i=(t||"").match(We.notSpaceStart))==null?void 0:i[0],r=e.replace(We.endingNewline,"")+` -`;return s?'
'+(n?r:Ct(r,!0))+`
-`:"
"+(n?r:Ct(r,!0))+`
-`}blockquote({tokens:e}){return`
+`), s = "", r = "", i = []; for (; n.length > 0;) { + let o = !1, a = [], l; for (l = 0; l < n.length; l++)if (this.rules.other.blockquoteStart.test(n[l])) a.push(n[l]), o = !0; else if (!o) a.push(n[l]); else break; n = n.slice(l); let f = a.join(` +`), u = f.replace(this.rules.other.blockquoteSetextReplace, ` + $1`).replace(this.rules.other.blockquoteSetextReplace2, ""); s = s ? `${s} +${f}` : f, r = r ? `${r} +${u}` : u; let d = this.lexer.state.top; if (this.lexer.state.top = !0, this.lexer.blockTokens(u, i, !0), this.lexer.state.top = d, n.length === 0) break; let g = i.at(-1); if ((g == null ? void 0 : g.type) === "code") break; if ((g == null ? void 0 : g.type) === "blockquote") { + let y = g, E = y.raw + ` +`+ n.join(` +`), x = this.blockquote(E); i[i.length - 1] = x, s = s.substring(0, s.length - y.raw.length) + x.raw, r = r.substring(0, r.length - y.text.length) + x.text; break + } else if ((g == null ? void 0 : g.type) === "list") { + let y = g, E = y.raw + ` +`+ n.join(` +`), x = this.list(E); i[i.length - 1] = x, s = s.substring(0, s.length - g.raw.length) + x.raw, r = r.substring(0, r.length - y.raw.length) + x.raw, n = E.substring(i.at(-1).raw.length).split(` +`); continue + } + } return { type: "blockquote", raw: s, tokens: i, text: r } + } + } list(e) { + let t = this.rules.block.list.exec(e); if (t) { + let n = t[1].trim(), s = n.length > 1, r = { type: "list", raw: "", ordered: s, start: s ? +n.slice(0, -1) : "", loose: !1, items: [] }; n = s ? `\\d{1,9}\\${n.slice(-1)}` : `\\${n}`, this.options.pedantic && (n = s ? n : "[*+-]"); let i = this.rules.other.listItemRegex(n), o = !1; for (; e;) { + let l = !1, f = "", u = ""; if (!(t = i.exec(e)) || this.rules.block.hr.test(e)) break; f = t[0], e = e.substring(f.length); let d = yf(t[2].split(` +`, 1)[0], t[1].length), g = e.split(` +`, 1)[0], y = !d.trim(), E = 0; if (this.options.pedantic ? (E = 2, u = d.trimStart()) : y ? E = t[1].length + 1 : (E = d.search(this.rules.other.nonSpaceChar), E = E > 4 ? 1 : E, u = d.slice(E), E += t[1].length), y && this.rules.other.blankLine.test(g) && (f += g + ` +`, e = e.substring(g.length + 1), l = !0), !l) { + let x = this.rules.other.nextBulletRegex(E), M = this.rules.other.hrRegex(E), m = this.rules.other.fencesBeginRegex(E), H = this.rules.other.headingBeginRegex(E), Y = this.rules.other.htmlBeginRegex(E), $ = this.rules.other.blockquoteBeginRegex(E); for (; e;) { + let z = e.split(` +`, 1)[0], O; if (g = z, this.options.pedantic ? (g = g.replace(this.rules.other.listReplaceNesting, " "), O = g) : O = g.replace(this.rules.other.tabCharGlobal, " "), m.test(g) || H.test(g) || Y.test(g) || $.test(g) || x.test(g) || M.test(g)) break; if (O.search(this.rules.other.nonSpaceChar) >= E || !g.trim()) u += ` +`+ O.slice(E); else { + if (y || d.replace(this.rules.other.tabCharGlobal, " ").search(this.rules.other.nonSpaceChar) >= 4 || m.test(d) || H.test(d) || M.test(d)) break; u += ` +`+ g + } y = !g.trim(), f += z + ` +`, e = e.substring(z.length + 1), d = O.slice(E) + } + } r.loose || (o ? r.loose = !0 : this.rules.other.doubleBlankLine.test(f) && (o = !0)), r.items.push({ type: "list_item", raw: f, task: !!this.options.gfm && this.rules.other.listIsTask.test(u), loose: !1, text: u, tokens: [] }), r.raw += f + } let a = r.items.at(-1); if (a) a.raw = a.raw.trimEnd(), a.text = a.text.trimEnd(); else return; r.raw = r.raw.trimEnd(); for (let l of r.items) { this.lexer.state.top = !1, l.tokens = this.lexer.blockTokens(l.text, []); let f = l.tokens[0]; if (l.task && ((f == null ? void 0 : f.type) === "text" || (f == null ? void 0 : f.type) === "paragraph")) { l.text = l.text.replace(this.rules.other.listReplaceTask, ""), f.raw = f.raw.replace(this.rules.other.listReplaceTask, ""), f.text = f.text.replace(this.rules.other.listReplaceTask, ""); for (let d = this.lexer.inlineQueue.length - 1; d >= 0; d--)if (this.rules.other.listIsTask.test(this.lexer.inlineQueue[d].src)) { this.lexer.inlineQueue[d].src = this.lexer.inlineQueue[d].src.replace(this.rules.other.listReplaceTask, ""); break } let u = this.rules.other.listTaskCheckbox.exec(l.raw); if (u) { let d = { type: "checkbox", raw: u[0] + " ", checked: u[0] !== "[ ]" }; l.checked = d.checked, r.loose ? l.tokens[0] && ["paragraph", "text"].includes(l.tokens[0].type) && "tokens" in l.tokens[0] && l.tokens[0].tokens ? (l.tokens[0].raw = d.raw + l.tokens[0].raw, l.tokens[0].text = d.raw + l.tokens[0].text, l.tokens[0].tokens.unshift(d)) : l.tokens.unshift({ type: "paragraph", raw: d.raw, text: d.raw, tokens: [d] }) : l.tokens.unshift(d) } } else l.task && (l.task = !1); if (!r.loose) { let u = l.tokens.filter(g => g.type === "space"), d = u.length > 0 && u.some(g => this.rules.other.anyLine.test(g.raw)); r.loose = d } } if (r.loose) for (let l of r.items) { l.loose = !0; for (let f of l.tokens) f.type === "text" && (f.type = "paragraph") } return r + } + } html(e) { let t = this.rules.block.html.exec(e); if (t) { let n = tl(t[0]); return { type: "html", block: !0, raw: n, pre: t[1] === "pre" || t[1] === "script" || t[1] === "style", text: n } } } def(e) { + let t = this.rules.block.def.exec(e); if (t) { + let n = t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal, " "), s = t[2] ? t[2].replace(this.rules.other.hrefBrackets, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "", r = t[3] ? t[3].substring(1, t[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : t[3]; return { + type: "def", tag: n, raw: Qt(t[0], ` +`), href: s, title: r + } + } + } table(e) { + var o; let t = this.rules.block.table.exec(e); if (!t || !this.rules.other.tableDelimiter.test(t[2])) return; let n = el(t[1]), s = t[2].replace(this.rules.other.tableAlignChars, "").split("|"), r = (o = t[3]) != null && o.trim() ? t[3].replace(this.rules.other.tableRowBlankLine, "").split(` +`) : [], i = { + type: "table", raw: Qt(t[0], ` +`), header: [], align: [], rows: [] + }; if (n.length === s.length) { for (let a of s) this.rules.other.tableAlignRight.test(a) ? i.align.push("right") : this.rules.other.tableAlignCenter.test(a) ? i.align.push("center") : this.rules.other.tableAlignLeft.test(a) ? i.align.push("left") : i.align.push(null); for (let a = 0; a < n.length; a++)i.header.push({ text: n[a], tokens: this.lexer.inline(n[a]), header: !0, align: i.align[a] }); for (let a of r) i.rows.push(el(a, i.header.length).map((l, f) => ({ text: l, tokens: this.lexer.inline(l), header: !1, align: i.align[f] }))); return i } + } lheading(e) { + let t = this.rules.block.lheading.exec(e); if (t) { + let n = t[1].trim(); return { + type: "heading", raw: Qt(t[0], ` +`), depth: t[2].charAt(0) === "=" ? 1 : 2, text: n, tokens: this.lexer.inline(n) + } + } + } paragraph(e) { + let t = this.rules.block.paragraph.exec(e); if (t) { + let n = t[1].charAt(t[1].length - 1) === ` +`? t[1].slice(0, -1) : t[1]; return { type: "paragraph", raw: t[0], text: n, tokens: this.lexer.inline(n) } + } + } text(e) { let t = this.rules.block.text.exec(e); if (t) return { type: "text", raw: t[0], text: t[0], tokens: this.lexer.inline(t[0]) } } escape(e) { let t = this.rules.inline.escape.exec(e); if (t) return { type: "escape", raw: t[0], text: t[1] } } tag(e) { let t = this.rules.inline.tag.exec(e); if (t) return !this.lexer.state.inLink && this.rules.other.startATag.test(t[0]) ? this.lexer.state.inLink = !0 : this.lexer.state.inLink && this.rules.other.endATag.test(t[0]) && (this.lexer.state.inLink = !1), !this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(t[0]) ? this.lexer.state.inRawBlock = !0 : this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(t[0]) && (this.lexer.state.inRawBlock = !1), { type: "html", raw: t[0], inLink: this.lexer.state.inLink, inRawBlock: this.lexer.state.inRawBlock, block: !1, text: t[0] } } link(e) { let t = this.rules.inline.link.exec(e); if (t) { let n = t[2].trim(); if (!this.options.pedantic && this.rules.other.startAngleBracket.test(n)) { if (!this.rules.other.endAngleBracket.test(n)) return; let i = Qt(n.slice(0, -1), "\\"); if ((n.length - i.length) % 2 === 0) return } else { let i = xf(t[2], "()"); if (i === -2) return; if (i > -1) { let o = (t[0].indexOf("!") === 0 ? 5 : 4) + t[1].length + i; t[2] = t[2].substring(0, i), t[0] = t[0].substring(0, o).trim(), t[3] = "" } } let s = t[2], r = ""; if (this.options.pedantic) { let i = this.rules.other.pedanticHrefTitle.exec(s); i && (s = i[1], r = i[3]) } else r = t[3] ? t[3].slice(1, -1) : ""; return s = s.trim(), this.rules.other.startAngleBracket.test(s) && (this.options.pedantic && !this.rules.other.endAngleBracket.test(n) ? s = s.slice(1) : s = s.slice(1, -1)), nl(t, { href: s && s.replace(this.rules.inline.anyPunctuation, "$1"), title: r && r.replace(this.rules.inline.anyPunctuation, "$1") }, t[0], this.lexer, this.rules) } } reflink(e, t) { let n; if ((n = this.rules.inline.reflink.exec(e)) || (n = this.rules.inline.nolink.exec(e))) { let s = (n[2] || n[1]).replace(this.rules.other.multipleSpaceGlobal, " "), r = t[s.toLowerCase()]; if (!r) { let i = n[0].charAt(0); return { type: "text", raw: i, text: i } } return nl(n, r, n[0], this.lexer, this.rules) } } emStrong(e, t, n = "") { let s = this.rules.inline.emStrongLDelim.exec(e); if (!(!s || !s[1] && !s[2] && !s[3] && !s[4] || s[4] && n.match(this.rules.other.unicodeAlphaNumeric)) && (!(s[1] || s[3]) || !n || this.rules.inline.punctuation.exec(n))) { let r = [...s[0]].length - 1, i, o, a = r, l = 0, f = s[0][0] === "*" ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd; for (f.lastIndex = 0, t = t.slice(-1 * e.length + r); (s = f.exec(t)) !== null;) { if (i = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], !i) continue; if (o = [...i].length, s[3] || s[4]) { a += o; continue } else if ((s[5] || s[6]) && r % 3 && !((r + o) % 3)) { l += o; continue } if (a -= o, a > 0) continue; o = Math.min(o, o + a + l); let u = [...s[0]][0].length, d = e.slice(0, r + s.index + u + o); if (Math.min(r, o) % 2) { let y = d.slice(1, -1); return { type: "em", raw: d, text: y, tokens: this.lexer.inlineTokens(y) } } let g = d.slice(2, -2); return { type: "strong", raw: d, text: g, tokens: this.lexer.inlineTokens(g) } } } } codespan(e) { let t = this.rules.inline.code.exec(e); if (t) { let n = t[2].replace(this.rules.other.newLineCharGlobal, " "), s = this.rules.other.nonSpaceChar.test(n), r = this.rules.other.startingSpaceChar.test(n) && this.rules.other.endingSpaceChar.test(n); return s && r && (n = n.substring(1, n.length - 1)), { type: "codespan", raw: t[0], text: n } } } br(e) { let t = this.rules.inline.br.exec(e); if (t) return { type: "br", raw: t[0] } } del(e, t, n = "") { let s = this.rules.inline.delLDelim.exec(e); if (s && (!s[1] || !n || this.rules.inline.punctuation.exec(n))) { let r = [...s[0]].length - 1, i, o, a = r, l = this.rules.inline.delRDelim; for (l.lastIndex = 0, t = t.slice(-1 * e.length + r); (s = l.exec(t)) !== null;) { if (i = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], !i || (o = [...i].length, o !== r)) continue; if (s[3] || s[4]) { a += o; continue } if (a -= o, a > 0) continue; o = Math.min(o, o + a); let f = [...s[0]][0].length, u = e.slice(0, r + s.index + f + o), d = u.slice(r, -r); return { type: "del", raw: u, text: d, tokens: this.lexer.inlineTokens(d) } } } } autolink(e) { let t = this.rules.inline.autolink.exec(e); if (t) { let n, s; return t[2] === "@" ? (n = t[1], s = "mailto:" + n) : (n = t[1], s = n), { type: "link", raw: t[0], text: n, href: s, tokens: [{ type: "text", raw: n, text: n }] } } } url(e) { var n, s; let t; if (t = this.rules.inline.url.exec(e)) { let r, i; if (t[2] === "@") r = t[0], i = "mailto:" + r; else { let o; do o = t[0], t[0] = (s = (n = this.rules.inline._backpedal.exec(t[0])) == null ? void 0 : n[0]) != null ? s : ""; while (o !== t[0]); r = t[0], t[1] === "www." ? i = "http://" + t[0] : i = t[0] } return { type: "link", raw: t[0], text: r, href: i, tokens: [{ type: "text", raw: r, text: r }] } } } inlineText(e) { let t = this.rules.inline.text.exec(e); if (t) { let n = this.lexer.state.inRawBlock; return { type: "text", raw: t[0], text: t[0], escaped: n } } } + }, yt = class Xr { + constructor(t) { _e(this, "tokens"); _e(this, "options"); _e(this, "state"); _e(this, "inlineQueue"); _e(this, "tokenizer"); this.tokens = [], this.tokens.links = Object.create(null), this.options = t || cn, this.options.tokenizer = this.options.tokenizer || new Ls, this.tokenizer = this.options.tokenizer, this.tokenizer.options = this.options, this.tokenizer.lexer = this, this.inlineQueue = [], this.state = { inLink: !1, inRawBlock: !1, top: !0 }; let n = { other: We, block: Os.normal, inline: Yn.normal }; this.options.pedantic ? (n.block = Os.pedantic, n.inline = Yn.pedantic) : this.options.gfm && (n.block = Os.gfm, this.options.breaks ? n.inline = Yn.breaks : n.inline = Yn.gfm), this.tokenizer.rules = n } static get rules() { return { block: Os, inline: Yn } } static lex(t, n) { return new Xr(n).lex(t) } static lexInline(t, n) { return new Xr(n).inlineTokens(t) } lex(t) { + t = t.replace(We.carriageReturn, ` +`), this.blockTokens(t, this.tokens); for (let n = 0; n < this.inlineQueue.length; n++) { let s = this.inlineQueue[n]; this.inlineTokens(s.src, s.tokens) } return this.inlineQueue = [], this.tokens + } blockTokens(t, n = [], s = !1) { + var i, o, a; this.tokenizer.lexer = this, this.options.pedantic && (t = t.replace(We.tabCharGlobal, " ").replace(We.spaceLine, "")); let r = 1 / 0; for (; t;) { + if (t.length < r) r = t.length; else { this.infiniteLoopError(t.charCodeAt(0)); break } let l; if ((o = (i = this.options.extensions) == null ? void 0 : i.block) != null && o.some(u => (l = u.call({ lexer: this }, t, n)) ? (t = t.substring(l.raw.length), n.push(l), !0) : !1)) continue; if (l = this.tokenizer.space(t)) { + t = t.substring(l.raw.length); let u = n.at(-1); l.raw.length === 1 && u !== void 0 ? u.raw += ` +`: n.push(l); continue + } if (l = this.tokenizer.code(t)) { + t = t.substring(l.raw.length); let u = n.at(-1); (u == null ? void 0 : u.type) === "paragraph" || (u == null ? void 0 : u.type) === "text" ? (u.raw += (u.raw.endsWith(` +`) ? "" : ` +`) + l.raw, u.text += ` +`+ l.text, this.inlineQueue.at(-1).src = u.text) : n.push(l); continue + } if (l = this.tokenizer.fences(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.heading(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.hr(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.blockquote(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.list(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.html(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.def(t)) { + t = t.substring(l.raw.length); let u = n.at(-1); (u == null ? void 0 : u.type) === "paragraph" || (u == null ? void 0 : u.type) === "text" ? (u.raw += (u.raw.endsWith(` +`) ? "" : ` +`) + l.raw, u.text += ` +`+ l.raw, this.inlineQueue.at(-1).src = u.text) : this.tokens.links[l.tag] || (this.tokens.links[l.tag] = { href: l.href, title: l.title }, n.push(l)); continue + } if (l = this.tokenizer.table(t)) { t = t.substring(l.raw.length), n.push(l); continue } if (l = this.tokenizer.lheading(t)) { t = t.substring(l.raw.length), n.push(l); continue } let f = t; if ((a = this.options.extensions) != null && a.startBlock) { let u = 1 / 0, d = t.slice(1), g; this.options.extensions.startBlock.forEach(y => { g = y.call({ lexer: this }, d), typeof g == "number" && g >= 0 && (u = Math.min(u, g)) }), u < 1 / 0 && u >= 0 && (f = t.substring(0, u + 1)) } if (this.state.top && (l = this.tokenizer.paragraph(f))) { + let u = n.at(-1); s && (u == null ? void 0 : u.type) === "paragraph" ? (u.raw += (u.raw.endsWith(` +`) ? "" : ` +`) + l.raw, u.text += ` +`+ l.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src = u.text) : n.push(l), s = f.length !== t.length, t = t.substring(l.raw.length); continue + } if (l = this.tokenizer.text(t)) { + t = t.substring(l.raw.length); let u = n.at(-1); (u == null ? void 0 : u.type) === "text" ? (u.raw += (u.raw.endsWith(` +`) ? "" : ` +`) + l.raw, u.text += ` +`+ l.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src = u.text) : n.push(l); continue + } if (t) { this.infiniteLoopError(t.charCodeAt(0)); break } + } return this.state.top = !0, n + } inline(t, n = []) { return this.inlineQueue.push({ src: t, tokens: n }), n } inlineTokens(t, n = []) { var f, u, d, g, y, E; this.tokenizer.lexer = this; let s = t, r = null; if (this.tokens.links) { let x = Object.keys(this.tokens.links); if (x.length > 0) for (; (r = this.tokenizer.rules.inline.reflinkSearch.exec(s)) !== null;)x.includes(r[0].slice(r[0].lastIndexOf("[") + 1, -1)) && (s = s.slice(0, r.index) + "[" + "a".repeat(r[0].length - 2) + "]" + s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex)) } for (; (r = this.tokenizer.rules.inline.anyPunctuation.exec(s)) !== null;)s = s.slice(0, r.index) + "++" + s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex); let i; for (; (r = this.tokenizer.rules.inline.blockSkip.exec(s)) !== null;)i = r[2] ? r[2].length : 0, s = s.slice(0, r.index + i) + "[" + "a".repeat(r[0].length - i - 2) + "]" + s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); s = (d = (u = (f = this.options.hooks) == null ? void 0 : f.emStrongMask) == null ? void 0 : u.call({ lexer: this }, s)) != null ? d : s; let o = !1, a = "", l = 1 / 0; for (; t;) { if (t.length < l) l = t.length; else { this.infiniteLoopError(t.charCodeAt(0)); break } o || (a = ""), o = !1; let x; if ((y = (g = this.options.extensions) == null ? void 0 : g.inline) != null && y.some(m => (x = m.call({ lexer: this }, t, n)) ? (t = t.substring(x.raw.length), n.push(x), !0) : !1)) continue; if (x = this.tokenizer.escape(t)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.tag(t)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.link(t)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.reflink(t, this.tokens.links)) { t = t.substring(x.raw.length); let m = n.at(-1); x.type === "text" && (m == null ? void 0 : m.type) === "text" ? (m.raw += x.raw, m.text += x.text) : n.push(x); continue } if (x = this.tokenizer.emStrong(t, s, a)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.codespan(t)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.br(t)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.del(t, s, a)) { t = t.substring(x.raw.length), n.push(x); continue } if (x = this.tokenizer.autolink(t)) { t = t.substring(x.raw.length), n.push(x); continue } if (!this.state.inLink && (x = this.tokenizer.url(t))) { t = t.substring(x.raw.length), n.push(x); continue } let M = t; if ((E = this.options.extensions) != null && E.startInline) { let m = 1 / 0, H = t.slice(1), Y; this.options.extensions.startInline.forEach($ => { Y = $.call({ lexer: this }, H), typeof Y == "number" && Y >= 0 && (m = Math.min(m, Y)) }), m < 1 / 0 && m >= 0 && (M = t.substring(0, m + 1)) } if (x = this.tokenizer.inlineText(M)) { t = t.substring(x.raw.length), x.raw.slice(-1) !== "_" && (a = x.raw.slice(-1)), o = !0; let m = n.at(-1); (m == null ? void 0 : m.type) === "text" ? (m.raw += x.raw, m.text += x.text) : n.push(x); continue } if (t) { this.infiniteLoopError(t.charCodeAt(0)); break } } return n } infiniteLoopError(t) { let n = "Infinite loop on byte: " + t; if (!this.options.silent) throw new Error(n) } + }, $s = class { + constructor(e) { _e(this, "options"); _e(this, "parser"); this.options = e || cn } space(e) { return "" } code({ text: e, lang: t, escaped: n }) { + var i; let s = (i = (t || "").match(We.notSpaceStart)) == null ? void 0 : i[0], r = e.replace(We.endingNewline, "") + ` +`; return s ? '
' + (n ? r : Ct(r, !0)) + `
+`: "
" + (n ? r : Ct(r, !0)) + `
+`} blockquote({ tokens: e }) { + return `
${this.parser.parse(e)}
-`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)} -`}hr(e){return`
-`}list(e){let t=e.ordered,n=e.start,s="";for(let o=0;o -`+s+" -`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • -`}checkbox({checked:e}){return" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    -`}table(e){let t="",n="";for(let r=0;r${s}`),` +`} html({ text: e }) { return e } def(e) { return "" } heading({ tokens: e, depth: t }) { + return `${this.parser.parseInline(e)} +`} hr(e) { + return `
    +`} list(e) { + let t = e.ordered, n = e.start, s = ""; for (let o = 0; o < e.items.length; o++) { let a = e.items[o]; s += this.listitem(a) } let r = t ? "ol" : "ul", i = t && n !== 1 ? ' start="' + n + '"' : ""; return "<" + r + i + `> +`+ s + " +`} listitem(e) { + return `
  • ${this.parser.parse(e.tokens)}
  • +`} checkbox({ checked: e }) { return " ' } paragraph({ tokens: e }) { + return `

    ${this.parser.parseInline(e)}

    +`} table(e) { + let t = "", n = ""; for (let r = 0; r < e.header.length; r++)n += this.tablecell(e.header[r]); t += this.tablerow({ text: n }); let s = ""; for (let r = 0; r < e.rows.length; r++) { let i = e.rows[r]; n = ""; for (let o = 0; o < i.length; o++)n += this.tablecell(i[o]); s += this.tablerow({ text: n }) } return s && (s = `${s}`), `
    -`+t+` -`+s+`
    -`}tablerow({text:e}){return` +`+ t + ` +`+ s + ` +`} tablerow({ text: e }) { + return ` ${e} -`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+` -`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${Ct(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),r=Jo(e);if(r===null)return s;e=r;let i='
    ",i}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let r=Jo(e);if(r===null)return Ct(n);e=r;let i=`${Ct(n)}{let l=o[a].flat(1/0);n=n.concat(this.walkTokens(l,t))}):o.tokens&&(n=n.concat(this.walkTokens(o.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(r=>{if(!r.name)throw new Error("extension name required");if("renderer"in r){let i=t.renderers[r.name];i?t.renderers[r.name]=function(...o){let a=r.renderer.apply(this,o);return a===!1&&(a=i.apply(this,o)),a}:t.renderers[r.name]=r.renderer}if("tokenizer"in r){if(!r.level||r.level!=="block"&&r.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let i=t[r.level];i?i.unshift(r.tokenizer):t[r.level]=[r.tokenizer],r.start&&(r.level==="block"?t.startBlock?t.startBlock.push(r.start):t.startBlock=[r.start]:r.level==="inline"&&(t.startInline?t.startInline.push(r.start):t.startInline=[r.start]))}"childTokens"in r&&r.childTokens&&(t.childTokens[r.name]=r.childTokens)}),s.extensions=t),n.renderer){let r=this.defaults.renderer||new $s(this.defaults);for(let i in n.renderer){if(!(i in r))throw new Error(`renderer '${i}' does not exist`);if(["options","parser"].includes(i))continue;let o=i,a=n.renderer[o],l=r[o];r[o]=(...f)=>{let u=a.apply(r,f);return u===!1&&(u=l.apply(r,f)),u||""}}s.renderer=r}if(n.tokenizer){let r=this.defaults.tokenizer||new Ls(this.defaults);for(let i in n.tokenizer){if(!(i in r))throw new Error(`tokenizer '${i}' does not exist`);if(["options","rules","lexer"].includes(i))continue;let o=i,a=n.tokenizer[o],l=r[o];r[o]=(...f)=>{let u=a.apply(r,f);return u===!1&&(u=l.apply(r,f)),u}}s.tokenizer=r}if(n.hooks){let r=this.defaults.hooks||new Zn;for(let i in n.hooks){if(!(i in r))throw new Error(`hook '${i}' does not exist`);if(["options","block"].includes(i))continue;let o=i,a=n.hooks[o],l=r[o];Zn.passThroughHooks.has(i)?r[o]=f=>{if(this.defaults.async&&Zn.passThroughHooksRespectAsync.has(i))return(async()=>{let d=await a.call(r,f);return l.call(r,d)})();let u=a.call(r,f);return l.call(r,u)}:r[o]=(...f)=>{if(this.defaults.async)return(async()=>{let d=await a.apply(r,f);return d===!1&&(d=await l.apply(r,f)),d})();let u=a.apply(r,f);return u===!1&&(u=l.apply(r,f)),u}}s.hooks=r}if(n.walkTokens){let r=this.defaults.walkTokens,i=n.walkTokens;s.walkTokens=function(o){let a=[];return a.push(i.call(this,o)),r&&(a=a.concat(r.call(this,o))),a}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return yt.lex(e,t!=null?t:this.defaults)}parser(e,t){return wt.parse(e,t!=null?t:this.defaults)}parseMarkdown(e){return(t,n)=>{let s={...n},r={...this.defaults,...s},i=this.onError(!!r.silent,!!r.async);if(this.defaults.async===!0&&s.async===!1)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||t===null)return i(new Error("marked(): input parameter is undefined or null"));if(typeof t!="string")return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));if(r.hooks&&(r.hooks.options=r,r.hooks.block=e),r.async)return(async()=>{let o=r.hooks?await r.hooks.preprocess(t):t,a=await(r.hooks?await r.hooks.provideLexer(e):e?yt.lex:yt.lexInline)(o,r),l=r.hooks?await r.hooks.processAllTokens(a):a;r.walkTokens&&await Promise.all(this.walkTokens(l,r.walkTokens));let f=await(r.hooks?await r.hooks.provideParser(e):e?wt.parse:wt.parseInline)(l,r);return r.hooks?await r.hooks.postprocess(f):f})().catch(i);try{r.hooks&&(t=r.hooks.preprocess(t));let o=(r.hooks?r.hooks.provideLexer(e):e?yt.lex:yt.lexInline)(t,r);r.hooks&&(o=r.hooks.processAllTokens(o)),r.walkTokens&&this.walkTokens(o,r.walkTokens);let a=(r.hooks?r.hooks.provideParser(e):e?wt.parse:wt.parseInline)(o,r);return r.hooks&&(a=r.hooks.postprocess(a)),a}catch(o){return i(o)}}}onError(e,t){return n=>{if(n.message+=` -Please report this to https://github.com/markedjs/marked.`,e){let s="

    An error occurred:

    "+Ct(n.message+"",!0)+"
    ";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}},pn=new vf;function xe(e,t){return pn.parse(e,t)}xe.options=xe.setOptions=function(e){return pn.setOptions(e),xe.defaults=pn.defaults,Ho(xe.defaults),xe},xe.getDefaults=Sr,xe.defaults=cn,xe.use=function(...e){return pn.use(...e),xe.defaults=pn.defaults,Ho(xe.defaults),xe},xe.walkTokens=function(e,t){return pn.walkTokens(e,t)},xe.parseInline=pn.parseInline,xe.Parser=wt,xe.parser=wt.parse,xe.Renderer=$s,xe.TextRenderer=Lr,xe.Lexer=yt,xe.lexer=yt.lex,xe.Tokenizer=Ls,xe.Hooks=Zn,xe.parse=xe,xe.options,xe.setOptions,xe.use,xe.walkTokens,xe.parseInline,wt.parse,yt.lex;function sl(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,s=Array(t);n2?s-2:0),i=2;i1?n-1:0),r=1;r1?n-1:0),r=1;r2&&arguments[2]!==void 0?arguments[2]:Xn;if(il&&il(e,null),!Xe(t))return e;let s=t.length;for(;s--;){let r=t[s];if(typeof r=="string"){const i=n(r);i!==r&&(Af(t)||(t[s]=i),r=i)}e[r]=!0}return e}function Df(e){for(let t=0;t/g),jf=dt(/\${[\w\W]*/g),Vf=dt(/^data-[\-\w.\u00B7-\uFFFF]+$/),qf=dt(/^aria-[\-\w]+$/),ml=dt(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Wf=dt(/^(?:\w+script|data):/i),Gf=dt(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Kf=dt(/^html$/i),Yf=dt(/^[a-z][.\w]*(-[.\w]+)+$/i),Rn={element:1,text:3,progressingInstruction:7,comment:8,document:9},Zf=function(){return typeof window=="undefined"?null:window},Xf=function(t,n){if(typeof t!="object"||typeof t.createPolicy!="function")return null;let s=null;const r="data-tt-policy-suffix";n&&n.hasAttribute(r)&&(s=n.getAttribute(r));const i="dompurify"+(s?"#"+s:"");try{return t.createPolicy(i,{createHTML(o){return o},createScriptURL(o){return o}})}catch{return null}},bl=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function xl(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:Zf();const t=q=>xl(q);if(t.version="3.4.3",t.removed=[],!e||!e.document||e.document.nodeType!==Rn.document||!e.Element)return t.isSupported=!1,t;let n=e.document;const s=n,r=s.currentScript,i=e.DocumentFragment,o=e.HTMLTemplateElement,a=e.Node,l=e.Element,f=e.NodeFilter,u=e.NamedNodeMap,d=u===void 0?e.NamedNodeMap||e.MozNamedAttrMap:u,g=e.HTMLFormElement,y=e.DOMParser,E=e.trustedTypes,x=l.prototype,M=An(x,"cloneNode"),m=An(x,"remove"),H=An(x,"nextSibling"),Y=An(x,"childNodes"),$=An(x,"parentNode");if(typeof o=="function"){const q=n.createElement("template");q.content&&q.content.ownerDocument&&(n=q.content.ownerDocument)}let z,O="";const P=n,ee=P.implementation,V=P.createNodeIterator,se=P.createDocumentFragment,fe=P.getElementsByTagName,D=s.importNode;let Z=bl();t.isSupported=typeof rl=="function"&&typeof $=="function"&&ee&&ee.createHTMLDocument!==void 0;const Se=zf,Ue=Uf,te=jf,C=Vf,pe=qf,Ge=Wf,re=Gf,B=Yf;let de=ml,ae=null;const ve=X({},[...pl,...Fr,...Br,...Hr,...dl]);let ge=null;const nt=X({},[...hl,...zr,...gl,...Ns]);let ye=Object.seal(Tn(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),ht=null,G=null;const Ce=Object.seal(Tn(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let Ft=!0,c=!0,h=!1,b=!0,k=!1,v=!0,_=!1,I=!1,R=!1,A=!1,S=!1,U=!1,L=!0,j=!1;const K="user-content-";let ne=!0,he=!1,ie={},ke=null;const $e=X({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let ot=null;const lt=X({},["audio","video","img","source","image","track"]);let Bt=null;const Jn=X({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),De="http://www.w3.org/1998/Math/MathML",Qe="http://www.w3.org/2000/svg",at="http://www.w3.org/1999/xhtml";let Ht=at,Ur=!1,jr=null;const hd=X({},[De,Qe,at],Nr);let Vr=X({},["mi","mo","mn","ms","mtext"]),qr=X({},["annotation-xml"]);const gd=X({},["title","style","font","a","script"]);let es=null;const md=["application/xhtml+xml","text/html"],bd="text/html";let Le=null,Cn=null;const xd=n.createElement("form"),Sl=function(p){return p instanceof RegExp||p instanceof Function},Wr=function(){let p=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(Cn&&Cn===p)return;(!p||typeof p!="object")&&(p={}),p=tt(p),es=md.indexOf(p.PARSER_MEDIA_TYPE)===-1?bd:p.PARSER_MEDIA_TYPE,Le=es==="application/xhtml+xml"?Nr:Xn,ae=Ae(p,"ALLOWED_TAGS")&&Xe(p.ALLOWED_TAGS)?X({},p.ALLOWED_TAGS,Le):ve,ge=Ae(p,"ALLOWED_ATTR")&&Xe(p.ALLOWED_ATTR)?X({},p.ALLOWED_ATTR,Le):nt,jr=Ae(p,"ALLOWED_NAMESPACES")&&Xe(p.ALLOWED_NAMESPACES)?X({},p.ALLOWED_NAMESPACES,Nr):hd,Bt=Ae(p,"ADD_URI_SAFE_ATTR")&&Xe(p.ADD_URI_SAFE_ATTR)?X(tt(Jn),p.ADD_URI_SAFE_ATTR,Le):Jn,ot=Ae(p,"ADD_DATA_URI_TAGS")&&Xe(p.ADD_DATA_URI_TAGS)?X(tt(lt),p.ADD_DATA_URI_TAGS,Le):lt,ke=Ae(p,"FORBID_CONTENTS")&&Xe(p.FORBID_CONTENTS)?X({},p.FORBID_CONTENTS,Le):$e,ht=Ae(p,"FORBID_TAGS")&&Xe(p.FORBID_TAGS)?X({},p.FORBID_TAGS,Le):tt({}),G=Ae(p,"FORBID_ATTR")&&Xe(p.FORBID_ATTR)?X({},p.FORBID_ATTR,Le):tt({}),ie=Ae(p,"USE_PROFILES")?p.USE_PROFILES&&typeof p.USE_PROFILES=="object"?tt(p.USE_PROFILES):p.USE_PROFILES:!1,Ft=p.ALLOW_ARIA_ATTR!==!1,c=p.ALLOW_DATA_ATTR!==!1,h=p.ALLOW_UNKNOWN_PROTOCOLS||!1,b=p.ALLOW_SELF_CLOSE_IN_ATTR!==!1,k=p.SAFE_FOR_TEMPLATES||!1,v=p.SAFE_FOR_XML!==!1,_=p.WHOLE_DOCUMENT||!1,A=p.RETURN_DOM||!1,S=p.RETURN_DOM_FRAGMENT||!1,U=p.RETURN_TRUSTED_TYPE||!1,R=p.FORCE_BODY||!1,L=p.SANITIZE_DOM!==!1,j=p.SANITIZE_NAMED_PROPS||!1,ne=p.KEEP_CONTENT!==!1,he=p.IN_PLACE||!1,de=Ff(p.ALLOWED_URI_REGEXP)?p.ALLOWED_URI_REGEXP:ml,Ht=typeof p.NAMESPACE=="string"?p.NAMESPACE:at,Vr=Ae(p,"MATHML_TEXT_INTEGRATION_POINTS")&&p.MATHML_TEXT_INTEGRATION_POINTS&&typeof p.MATHML_TEXT_INTEGRATION_POINTS=="object"?tt(p.MATHML_TEXT_INTEGRATION_POINTS):X({},["mi","mo","mn","ms","mtext"]),qr=Ae(p,"HTML_INTEGRATION_POINTS")&&p.HTML_INTEGRATION_POINTS&&typeof p.HTML_INTEGRATION_POINTS=="object"?tt(p.HTML_INTEGRATION_POINTS):X({},["annotation-xml"]);const T=Ae(p,"CUSTOM_ELEMENT_HANDLING")&&p.CUSTOM_ELEMENT_HANDLING&&typeof p.CUSTOM_ELEMENT_HANDLING=="object"?tt(p.CUSTOM_ELEMENT_HANDLING):Tn(null);if(ye=Tn(null),Ae(T,"tagNameCheck")&&Sl(T.tagNameCheck)&&(ye.tagNameCheck=T.tagNameCheck),Ae(T,"attributeNameCheck")&&Sl(T.attributeNameCheck)&&(ye.attributeNameCheck=T.attributeNameCheck),Ae(T,"allowCustomizedBuiltInElements")&&typeof T.allowCustomizedBuiltInElements=="boolean"&&(ye.allowCustomizedBuiltInElements=T.allowCustomizedBuiltInElements),k&&(c=!1),S&&(A=!0),ie&&(ae=X({},dl),ge=Tn(null),ie.html===!0&&(X(ae,pl),X(ge,hl)),ie.svg===!0&&(X(ae,Fr),X(ge,zr),X(ge,Ns)),ie.svgFilters===!0&&(X(ae,Br),X(ge,zr),X(ge,Ns)),ie.mathMl===!0&&(X(ae,Hr),X(ge,gl),X(ge,Ns))),Ce.tagCheck=null,Ce.attributeCheck=null,Ae(p,"ADD_TAGS")&&(typeof p.ADD_TAGS=="function"?Ce.tagCheck=p.ADD_TAGS:Xe(p.ADD_TAGS)&&(ae===ve&&(ae=tt(ae)),X(ae,p.ADD_TAGS,Le))),Ae(p,"ADD_ATTR")&&(typeof p.ADD_ATTR=="function"?Ce.attributeCheck=p.ADD_ATTR:Xe(p.ADD_ATTR)&&(ge===nt&&(ge=tt(ge)),X(ge,p.ADD_ATTR,Le))),Ae(p,"ADD_URI_SAFE_ATTR")&&Xe(p.ADD_URI_SAFE_ATTR)&&X(Bt,p.ADD_URI_SAFE_ATTR,Le),Ae(p,"FORBID_CONTENTS")&&Xe(p.FORBID_CONTENTS)&&(ke===$e&&(ke=tt(ke)),X(ke,p.FORBID_CONTENTS,Le)),Ae(p,"ADD_FORBID_CONTENTS")&&Xe(p.ADD_FORBID_CONTENTS)&&(ke===$e&&(ke=tt(ke)),X(ke,p.ADD_FORBID_CONTENTS,Le)),ne&&(ae["#text"]=!0),_&&X(ae,["html","head","body"]),ae.table&&(X(ae,["tbody"]),delete ht.tbody),p.TRUSTED_TYPES_POLICY){if(typeof p.TRUSTED_TYPES_POLICY.createHTML!="function")throw Ds('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof p.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw Ds('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');z=p.TRUSTED_TYPES_POLICY,O=z.createHTML("")}else z===void 0&&(z=Xf(E,r)),z!==null&&typeof O=="string"&&(O=z.createHTML(""));Ze&&Ze(p),Cn=p},El=X({},[...Fr,...Br,...Bf]),Al=X({},[...Hr,...Hf]),yd=function(p){let T=$(p);(!T||!T.tagName)&&(T={namespaceURI:Ht,tagName:"template"});const N=Xn(p.tagName),ce=Xn(T.tagName);return jr[p.namespaceURI]?p.namespaceURI===Qe?T.namespaceURI===at?N==="svg":T.namespaceURI===De?N==="svg"&&(ce==="annotation-xml"||Vr[ce]):!!El[N]:p.namespaceURI===De?T.namespaceURI===at?N==="math":T.namespaceURI===Qe?N==="math"&&qr[ce]:!!Al[N]:p.namespaceURI===at?T.namespaceURI===Qe&&!qr[ce]||T.namespaceURI===De&&!Vr[ce]?!1:!Al[N]&&(gd[N]||!El[N]):!!(es==="application/xhtml+xml"&&jr[p.namespaceURI]):!1},vt=function(p){Sn(t.removed,{element:p});try{$(p).removeChild(p)}catch{m(p)}},dn=function(p,T){try{Sn(t.removed,{attribute:T.getAttributeNode(p),from:T})}catch{Sn(t.removed,{attribute:null,from:T})}if(T.removeAttribute(p),p==="is")if(A||S)try{vt(T)}catch{}else try{T.setAttribute(p,"")}catch{}},Rl=function(p){let T=null,N=null;if(R)p=""+p;else{const Me=al(p,/^[\r\n\t ]+/);N=Me&&Me[0]}es==="application/xhtml+xml"&&Ht===at&&(p=''+p+"");const ce=z?z.createHTML(p):p;if(Ht===at)try{T=new y().parseFromString(ce,es)}catch{}if(!T||!T.documentElement){T=ee.createDocument(Ht,"template",null);try{T.documentElement.innerHTML=Ur?O:ce}catch{}}const Ne=T.body||T.documentElement;return p&&N&&Ne.insertBefore(n.createTextNode(N),Ne.childNodes[0]||null),Ht===at?fe.call(T,_?"html":"body")[0]:_?T.documentElement:Ne},Cl=function(p){return V.call(p.ownerDocument||p,p,f.SHOW_ELEMENT|f.SHOW_COMMENT|f.SHOW_TEXT|f.SHOW_PROCESSING_INSTRUCTION|f.SHOW_CDATA_SECTION,null)},Gr=function(p){return p instanceof g&&(typeof p.nodeName!="string"||typeof p.textContent!="string"||typeof p.removeChild!="function"||!(p.attributes instanceof d)||typeof p.removeAttribute!="function"||typeof p.setAttribute!="function"||typeof p.namespaceURI!="string"||typeof p.insertBefore!="function"||typeof p.hasChildNodes!="function")},Kr=function(p){return typeof a=="function"&&p instanceof a};function zt(q,p,T){kn(q,N=>{N.call(t,p,T,Cn)})}const Pl=function(p){let T=null;if(zt(Z.beforeSanitizeElements,p,null),Gr(p))return vt(p),!0;const N=Le(p.nodeName);if(zt(Z.uponSanitizeElement,p,{tagName:N,allowedTags:ae}),v&&p.hasChildNodes()&&!Kr(p.firstElementChild)&&ze(/<[/\w!]/g,p.innerHTML)&&ze(/<[/\w!]/g,p.textContent)||v&&p.namespaceURI===at&&N==="style"&&Kr(p.firstElementChild)||p.nodeType===Rn.progressingInstruction||v&&p.nodeType===Rn.comment&&ze(/<[/\w]/g,p.data))return vt(p),!0;if(ht[N]||!(Ce.tagCheck instanceof Function&&Ce.tagCheck(N))&&!ae[N]){if(!ht[N]&&Ml(N)&&(ye.tagNameCheck instanceof RegExp&&ze(ye.tagNameCheck,N)||ye.tagNameCheck instanceof Function&&ye.tagNameCheck(N)))return!1;if(ne&&!ke[N]){const ce=$(p)||p.parentNode,Ne=Y(p)||p.childNodes;if(Ne&&ce){const Me=Ne.length;for(let st=Me-1;st>=0;--st){const gt=M(Ne[st],!0);ce.insertBefore(gt,H(p))}}}return vt(p),!0}return p instanceof l&&!yd(p)||(N==="noscript"||N==="noembed"||N==="noframes")&&ze(/<\/no(script|embed|frames)/i,p.innerHTML)?(vt(p),!0):(k&&p.nodeType===Rn.text&&(T=p.textContent,kn([Se,Ue,te],ce=>{T=En(T,ce," ")}),p.textContent!==T&&(Sn(t.removed,{element:p.cloneNode()}),p.textContent=T)),zt(Z.afterSanitizeElements,p,null),!1)},Il=function(p,T,N){if(G[T]||L&&(T==="id"||T==="name")&&(N in n||N in xd))return!1;const ce=ge[T]||Ce.attributeCheck instanceof Function&&Ce.attributeCheck(T,p);if(!(c&&!G[T]&&ze(C,T))){if(!(Ft&&ze(pe,T))){if(!ce||G[T]){if(!(Ml(p)&&(ye.tagNameCheck instanceof RegExp&&ze(ye.tagNameCheck,p)||ye.tagNameCheck instanceof Function&&ye.tagNameCheck(p))&&(ye.attributeNameCheck instanceof RegExp&&ze(ye.attributeNameCheck,T)||ye.attributeNameCheck instanceof Function&&ye.attributeNameCheck(T,p))||T==="is"&&ye.allowCustomizedBuiltInElements&&(ye.tagNameCheck instanceof RegExp&&ze(ye.tagNameCheck,N)||ye.tagNameCheck instanceof Function&&ye.tagNameCheck(N))))return!1}else if(!Bt[T]){if(!ze(de,En(N,re,""))){if(!((T==="src"||T==="xlink:href"||T==="href")&&p!=="script"&&ul(N,"data:")===0&&ot[p])){if(!(h&&!ze(Ge,En(N,re,"")))){if(N)return!1}}}}}}return!0},wd=X({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),Ml=function(p){return!wd[Xn(p)]&&ze(B,p)},Ol=function(p){zt(Z.beforeSanitizeAttributes,p,null);const T=p.attributes;if(!T||Gr(p))return;const N={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ge,forceKeepAttr:void 0};let ce=T.length;for(;ce--;){const Ne=T[ce],Me=Ne.name,st=Ne.namespaceURI,gt=Ne.value,_t=Le(Me),Zr=gt;let Fe=Me==="value"?Zr:Mf(Zr);if(N.attrName=_t,N.attrValue=Fe,N.keepAttr=!0,N.forceKeepAttr=void 0,zt(Z.uponSanitizeAttribute,p,N),Fe=N.attrValue,j&&(_t==="id"||_t==="name")&&ul(Fe,K)!==0&&(dn(Me,p),Fe=K+Fe),v&&ze(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,Fe)){dn(Me,p);continue}if(_t==="attributename"&&al(Fe,"href")){dn(Me,p);continue}if(N.forceKeepAttr)continue;if(!N.keepAttr){dn(Me,p);continue}if(!b&&ze(/\/>/i,Fe)){dn(Me,p);continue}k&&kn([Se,Ue,te],$l=>{Fe=En(Fe,$l," ")});const Ll=Le(p.nodeName);if(!Il(Ll,_t,Fe)){dn(Me,p);continue}if(z&&typeof E=="object"&&typeof E.getAttributeType=="function"&&!st)switch(E.getAttributeType(Ll,_t)){case"TrustedHTML":{Fe=z.createHTML(Fe);break}case"TrustedScriptURL":{Fe=z.createScriptURL(Fe);break}}if(Fe!==Zr)try{st?p.setAttributeNS(st,Me,Fe):p.setAttribute(Me,Fe),Gr(p)?vt(p):ll(t.removed)}catch{dn(Me,p)}}zt(Z.afterSanitizeAttributes,p,null)},Yr=function(p){let T=null;const N=Cl(p);for(zt(Z.beforeSanitizeShadowDOM,p,null);T=N.nextNode();)zt(Z.uponSanitizeShadowNode,T,null),Pl(T),Ol(T),T.content instanceof i&&Yr(T.content);zt(Z.afterSanitizeShadowDOM,p,null)},Bs=function(p){if(p.nodeType===Rn.element&&p.shadowRoot instanceof i){const ce=p.shadowRoot;Bs(ce),Yr(ce)}const T=p.childNodes;if(!T)return;const N=[];kn(T,ce=>{Sn(N,ce)});for(const ce of N)Bs(ce)};return t.sanitize=function(q){let p=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},T=null,N=null,ce=null,Ne=null;if(Ur=!q,Ur&&(q=""),typeof q!="string"&&!Kr(q)&&(q=Nf(q),typeof q!="string"))throw Ds("dirty is not a string, aborting");if(!t.isSupported)return q;if(I||Wr(p),t.removed=[],typeof q=="string"&&(he=!1),he){const gt=q.nodeName;if(typeof gt=="string"){const _t=Le(gt);if(!ae[_t]||ht[_t])throw Ds("root node is forbidden and cannot be sanitized in-place")}Bs(q)}else if(q instanceof a)T=Rl(""),N=T.ownerDocument.importNode(q,!0),N.nodeType===Rn.element&&N.nodeName==="BODY"||N.nodeName==="HTML"?T=N:T.appendChild(N),Bs(N);else{if(!A&&!k&&!_&&q.indexOf("<")===-1)return z&&U?z.createHTML(q):q;if(T=Rl(q),!T)return A?null:U?O:""}T&&R&&vt(T.firstChild);const Me=Cl(he?q:T);for(;ce=Me.nextNode();)Pl(ce),Ol(ce),ce.content instanceof i&&Yr(ce.content);if(he)return q;if(A){if(k){T.normalize();let gt=T.innerHTML;kn([Se,Ue,te],_t=>{gt=En(gt,_t," ")}),T.innerHTML=gt}if(S)for(Ne=se.call(T.ownerDocument);T.firstChild;)Ne.appendChild(T.firstChild);else Ne=T;return(ge.shadowroot||ge.shadowrootmode)&&(Ne=D.call(s,Ne,!0)),Ne}let st=_?T.outerHTML:T.innerHTML;return _&&ae["!doctype"]&&T.ownerDocument&&T.ownerDocument.doctype&&T.ownerDocument.doctype.name&&ze(Kf,T.ownerDocument.doctype.name)&&(st=" -`+st),k&&kn([Se,Ue,te],gt=>{st=En(st,gt," ")}),z&&U?z.createHTML(st):st},t.setConfig=function(){let q=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Wr(q),I=!0},t.clearConfig=function(){Cn=null,I=!1},t.isValidAttribute=function(q,p,T){Cn||Wr({});const N=Le(q),ce=Le(p);return Il(N,ce,T)},t.addHook=function(q,p){typeof p=="function"&&Sn(Z[q],p)},t.removeHook=function(q,p){if(p!==void 0){const T=Pf(Z[q],p);return T===-1?void 0:If(Z[q],T,1)[0]}return ll(Z[q])},t.removeHooks=function(q){Z[q]=[]},t.removeAllHooks=function(){Z=bl()},t}var Qf=xl();const Jf={key:1,class:"flex min-w-0 max-w-[calc(100%-2.5rem)] flex-1 flex-col max-[600px]:max-w-[calc(100%-2.25rem)]"},ep={key:0,class:"flex w-fit flex-col items-start gap-1"},tp=["aria-label"],np={class:"inline-flex items-center gap-1.5"},sp={class:"text-[8px] font-semibold tracking-[0.12em] uppercase text-[#3a67c9]"},rp={key:1,class:"flex w-fit max-w-full flex-col items-start gap-2"},ip={class:"chat-card relative w-fit max-w-full whitespace-pre-line rounded-[10px_10px_10px_3px] px-4 py-3 text-xs leading-relaxed wrap-anywhere text-slate-900"},op=["innerHTML"],lp={key:0,class:"pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-[10px] bg-linear-to-t from-white via-white/92 to-white/0","aria-hidden":"true"},ap={key:0,class:"flex flex-wrap items-center gap-2"},up=["title","aria-label"],cp={key:1,class:"flex flex-wrap items-center"},fp=["title","aria-label"],pp={viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},dp=["innerHTML"],yl={__name:"ChatMessage",props:{message:{type:Object,required:!0},autoReadEnabled:{type:Boolean,default:!1},ttsConfig:{type:Object,default:()=>({enableVoiceChat:!1,pollyAvailable:!1,usePolly:!0,voiceId:"Zayd"})}},setup(e){const t=e,n=J(!1),s=J(null),r=J(!1),i=J(!1),o=Ee(()=>typeof window!="undefined"&&"speechSynthesis"in window&&"SpeechSynthesisUtterance"in window);function a(O){typeof window!="undefined"&&window.dispatchEvent(new CustomEvent("changai-tts-provider",{detail:{provider:O}}))}function l(O){if(typeof O!="string")return"";const P=O.replace(/[\u{1F000}-\u{1FFFF}]/gu,"").replace(/[\u{2600}-\u{26FF}]/gu,"").replace(/[\u{2700}-\u{27BF}]/gu,"").replace(/\*\*(.*?)\*\*/g,"$1").replace(/\*(.*?)\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/#{1,6}\s+/g,"").replace(/[-*+]\s+/g,"").replace(/\[([^\]]+)\]\([^)]+\)/g,"$1").replace(/\s+/g," ");return P.includes("<")?(new DOMParser().parseFromString(O,"text/html").body.textContent||"").replace(/\s+/g," ").trim():P.trim()}function f(){o.value&&window.speechSynthesis.cancel(),s.value&&(s.value.pause(),s.value.src="",s.value=null),n.value=!1}function u(){var O,P,ee;if(i.value=!i.value,i.value)f();else{const V=M.value;if(!t.autoReadEnabled||!((O=t.ttsConfig)!=null&&O.enableVoiceChat)||!V||E())return;if((P=t.ttsConfig)!=null&&P.pollyAvailable&&((ee=t.ttsConfig)!=null&&ee.usePolly)){g(V).catch(se=>{d(V)});return}d(V)}}function d(O){if(!o.value||!O)return;window.dispatchEvent(new CustomEvent("changai-tts-stop")),window.speechSynthesis.cancel();const P=new SpeechSynthesisUtterance(O);P.rate=1,P.pitch=1,P.onend=()=>{n.value=!1},P.onerror=()=>{n.value=!1},n.value=!0,a("browser"),window.speechSynthesis.speak(P)}async function g(O){var fe;const P=await Oc(O,((fe=t.ttsConfig)==null?void 0:fe.voiceId)||"Zayd");if(!(P!=null&&P.ok)||!(P!=null&&P.audio_base64))throw new Error((P==null?void 0:P.error)||"Polly synthesis failed");window.dispatchEvent(new CustomEvent("changai-tts-stop")),f();const ee=(P==null?void 0:P.mime_type)||"audio/mpeg",V=new Audio(`data:${ee};base64,${P.audio_base64}`);s.value=V,n.value=!0;let se=!1;V.onplay=()=>{se=!0,a("polly")},V.onended=()=>{s.value===V&&(s.value=null),n.value=!1},V.onerror=()=>{s.value===V&&(s.value=null),n.value=!1},await V.play(),se||a("polly")}function y(){f()}function E(){var O;return!!((O=t.message)!=null&&O.isStatus)}function x(O){const P=O.target.closest("a");!P||!P.href||(O.preventDefault(),O.stopPropagation(),window.open(P.href,"_blank","noopener,noreferrer"))}const M=Ee(()=>{var O;return l(((O=t.message)==null?void 0:O.text)||"")}),m=Ee(()=>{var O;return((O=t.message)==null?void 0:O.role)!=="user"&&E()}),H=Ee(()=>{var O;return(O=t.message)!=null&&O.isStatus?t.message.statusType==="support"?"Sending to support":M.value||"Thinking":""}),Y=Ee(()=>{var ee;if(((ee=t.message)==null?void 0:ee.role)==="user"||m.value)return!1;const O=M.value,P=O.split(/\n+/).filter(Boolean).length;return O.length>520||P>8}),$=Ee(()=>{var O,P;return((O=t.message)==null?void 0:O.role)!=="user"&&!m.value&&((P=t.ttsConfig)==null?void 0:P.enableVoiceChat)}),z=Ee(()=>{var P;const O=((P=t.message)==null?void 0:P.text)||"";return Qf.sanitize(xe.parse(O))});return on(()=>t.message.text,async(O,P)=>{var se,fe,D;if(!t.autoReadEnabled||t.message.role==="user"||i.value)return;if(!((se=t.ttsConfig)!=null&&se.enableVoiceChat)){a("off");return}const ee=l(O);if(!ee||E())return;const V=l(P||"");if(ee!==V){if((fe=t.ttsConfig)!=null&&fe.pollyAvailable&&((D=t.ttsConfig)!=null&&D.usePolly))try{await g(ee);return}catch{}d(ee)}}),on(()=>t.message.text,()=>{r.value=!1,i.value=!1}),zn(()=>{typeof window!="undefined"&&window.addEventListener("changai-tts-stop",y)}),ys(()=>{typeof window!="undefined"&&window.removeEventListener("changai-tts-stop",y),n.value&&f()}),(O,P)=>(F(),W("div",{class:we(["motion-safe:animate-fade-rise flex w-full gap-1.5",e.message.role==="user"?"flex-col items-end":"items-start"])},[e.message.role!=="user"?(F(),Et(Fo,{key:0})):qe("",!0),e.message.role!=="user"?(F(),W("div",Jf,[m.value?(F(),W("div",ep,[w("div",{class:"chat-card inline-flex w-fit rounded-[10px_10px_10px_3px] px-3 py-2",role:"status","aria-live":"polite","aria-label":H.value},[w("div",np,[P[1]||(P[1]=w("span",{class:"relative inline-flex h-4 w-4 shrink-0 items-center justify-center"},[w("span",{class:"absolute inset-0 rounded-full border border-transparent border-t-[#4b89ff] border-r-[#4b89ff]/70 animate-gemini-arc"}),w("svg",{viewBox:"0 0 24 24",class:"relative h-3 w-3 text-[#4b89ff] animate-gemini-spark","aria-hidden":"true"},[w("path",{fill:"currentColor",d:"M12 2.8c.52 3.22 1.6 5.66 3.22 7.28 1.62 1.62 4.06 2.7 7.28 3.22-3.22.52-5.66 1.6-7.28 3.22-1.62 1.62-2.7 4.06-3.22 7.28-.52-3.22-1.6-5.66-3.22-7.28-1.62-1.62-4.06-2.7-7.28-3.22 3.22-.52 5.66-1.6 7.28-3.22 1.62-1.62 2.7-4.06 3.22-7.28Z"})])],-1)),w("span",sp,Je(H.value),1)])],8,tp)])):(F(),W("div",rp,[w("div",ip,[w("div",{class:we(["overflow-x-auto",Y.value&&!r.value?"max-h-48 overflow-y-hidden":""]),innerHTML:z.value,onClick:x},null,10,op),Y.value&&!r.value?(F(),W("div",lp)):qe("",!0)]),Y.value?(F(),W("div",ap,[w("button",{type:"button",class:"inline-flex items-center rounded-full border border-slate-200 bg-white px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.08em] text-slate-600 transition-colors duration-200 hover:border-brand-200 hover:text-brand-600",title:r.value?"Collapse response":"Expand response","aria-label":r.value?"Collapse response":"Expand response",onClick:P[0]||(P[0]=ee=>r.value=!r.value)},Je(r.value?"Collapse":"Expand"),9,up)])):qe("",!0),$.value?(F(),W("div",cp,[w("button",{type:"button",class:we(["inline-flex h-8 w-8 items-center justify-center rounded-full border transition-colors duration-200",i.value?"border-red-200 bg-red-50 text-red-600 hover:border-red-300 hover:bg-red-100":"border-green-200 bg-green-50 text-green-600 hover:border-green-300 hover:bg-green-100"]),title:i.value?"Unmute voice playback":"Mute voice playback","aria-label":i.value?"Unmute voice playback":"Mute voice playback",onClick:u},[(F(),W("svg",pp,[P[6]||(P[6]=w("path",{d:"M11 5L6 9H3v6h3l5 4V5Z"},null,-1)),i.value?(F(),W(He,{key:0},[P[2]||(P[2]=w("path",{d:"M15 9l4 6"},null,-1)),P[3]||(P[3]=w("path",{d:"M19 9l-4 6"},null,-1))],64)):(F(),W(He,{key:1},[P[4]||(P[4]=w("path",{d:"M15 10a3 3 0 0 1 0 4"},null,-1)),P[5]||(P[5]=w("path",{d:"M17.5 7.5a6 6 0 0 1 0 9"},null,-1))],64))]))],10,fp)])):qe("",!0)]))])):(F(),W("div",{key:2,class:"w-fit max-w-[85%] whitespace-pre-line rounded-[13px_13px_3px_13px] bg-linear-to-br from-brand-500 to-brand-600 px-4 py-3 text-[11px] leading-relaxed wrap-anywhere text-white shadow-[0_14px_30px_-18px_rgba(109,79,194,0.85)] max-[600px]:max-w-[88%]",innerHTML:z.value},null,8,dp))],2))}},hp={class:"flex flex-col gap-4 sm:gap-5"},gp={class:"motion-safe:animate-fade-rise flex w-full items-start gap-1.5"},mp={__name:"ChatTab",props:{messages:{type:Array,required:!0},autoReadEnabled:{type:Boolean,default:!1},ttsConfig:{type:Object,required:!0}},setup(e){return(t,n)=>(F(),W("div",hp,[w("div",gp,[Oe(Fo),n[0]||(n[0]=w("p",{class:"w-fit max-w-[calc(100%-2.5rem)] whitespace-pre-line rounded-[10px_10px_10px_3px] bg-brand-50 px-4 py-3 text-xs leading-relaxed wrap-anywhere text-slate-900 max-[600px]:max-w-[calc(100%-2.25rem)]"},[Ss(" Hello there 👋 I am ChangAI from "),w("a",{target:"_blank",href:"https://erpgulf.com",rel:"noopener noreferrer",style:{color:"#1e90ff"}},"ERPGulf.com"),Ss(", your ERP assistant."),w("br"),w("a",{target:"_blank",href:"https://app.erpgulf.com/en/articles/chang-ai-quick-start-guide",rel:"noopener noreferrer",style:{color:"#1e90ff"}},"ChangAI Quick Start Guide - Click here.")],-1))]),(F(!0),W(He,null,ws(e.messages,(s,r)=>(F(),Et(yl,{key:r,message:s,autoReadEnabled:e.autoReadEnabled,ttsConfig:e.ttsConfig},null,8,["message","autoReadEnabled","ttsConfig"]))),128))]))}};function wl(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function vl(e){var t,n;return(e==null?void 0:e.message)||((t=e==null?void 0:e.responseJSON)==null?void 0:t.exception)||((n=e==null?void 0:e.responseJSON)==null?void 0:n.message)||(e==null?void 0:e.responseText)||String(e)}function bp(e){return typeof e=="string"?e:e&&typeof e=="object"?e.error?`⚠️ ${e.error}`:e.answer||e.text||"":""}const xp={key:0,class:"rounded-lg bg-brand-50 px-4 py-3 text-xs text-black"},yp={class:"whitespace-pre-wrap wrap-anywhere text-[11px] leading-relaxed text-black"},wp={key:1,class:"mb-3 min-w-0 overflow-x-auto rounded-lg bg-brand-50 p-2 text-[11px]"},vp={class:"whitespace-pre-wrap wrap-anywhere text-[11px] leading-relaxed text-black"},_p={__name:"DebugTab",props:{logs:{type:Array,required:!0},currentDebug:{type:Object,default:null}},setup(e){const t=new Set(["gemini_json_content","private_key","private_key_id","client_secret","client_id","aws_access_key","aws_secret_key","api_key","token","access_token","refresh_token","password","secret","authorization","embed_version_id","llm_version_id","entity_retriever","retriever","deploy_url","support_api_url","get_ticket_details_url"]);function n(r,i=0){if(i>10||r===null||r===void 0||typeof r=="string"||typeof r=="number"||typeof r=="boolean")return r;if(Array.isArray(r))return r.map(o=>n(o,i+1));if(typeof r=="object"){const o={};for(const[a,l]of Object.entries(r))t.has(a.toLowerCase())||(o[a]=n(l,i+1));return o}return r}function s(r){return wl(n(r))}return(r,i)=>(F(),W("div",null,[e.logs.length===0?(F(),W("p",xp,"No debug data yet.")):qe("",!0),(F(!0),W(He,null,ws(e.logs,(o,a)=>(F(),W("div",{key:a,class:"mb-3 min-w-0 overflow-x-auto rounded-lg bg-gray-100 p-2 text-[11px]"},[w("pre",yp,Je(s(o)),1)]))),128)),e.currentDebug?(F(),W("div",wp,[w("pre",vp,Je(s(e.currentDebug)),1)])):qe("",!0)]))}},Tp={class:"flex flex-col gap-4 sm:gap-5"},kp={key:0,class:"chat-card motion-safe:animate-fade-rise rounded-lg px-4 py-3 text-xs text-slate-900"},Sp={__name:"SupportTab",props:{messages:{type:Array,required:!0},autoReadEnabled:{type:Boolean,default:!1},ttsConfig:{type:Object,required:!0}},setup(e){return(t,n)=>(F(),W("div",Tp,[e.messages.length===0?(F(),W("p",kp,"Send a message to Support.")):qe("",!0),(F(!0),W(He,null,ws(e.messages,(s,r)=>(F(),Et(yl,{key:r,message:s,autoReadEnabled:e.autoReadEnabled,ttsConfig:e.ttsConfig},null,8,["message","autoReadEnabled","ttsConfig"]))),128))]))}},Ep={class:"flex flex-col gap-4"},Ap={class:"chat-card motion-safe:animate-fade-rise rounded-xl p-4"},Rp={class:"flex items-start justify-between gap-4"},Cp=["aria-pressed","title"],Pp={class:"chat-card motion-safe:animate-fade-rise rounded-xl p-4"},Ip={class:"flex items-start justify-between gap-4"},Mp={class:"mt-2 text-[11px] text-slate-500"},Op={key:0,class:"mt-1 text-[11px] text-slate-500"},Lp={key:1,class:"mt-1 text-[11px] text-slate-500"},$p=["aria-pressed","disabled"],Dp={key:0,class:"mt-3 rounded-md bg-amber-50 px-2.5 py-2 text-xs text-amber-700"},Np={key:1,class:"mt-3 rounded-md bg-amber-50 px-2.5 py-2 text-xs text-amber-700"},Fp={class:"chat-card motion-safe:animate-fade-rise rounded-xl p-4"},Bp={class:"flex items-start justify-between gap-4"},Hp=["aria-pressed","title"],zp={class:"chat-card motion-safe:animate-fade-rise rounded-xl p-4"},Up={class:"flex items-start justify-between gap-4"},jp=["aria-pressed","title"],Vp={__name:"SettingsTab",props:{autoReadEnabled:{type:Boolean,required:!0},ttsConfig:{type:Object,required:!0},settings:{type:Object,default:null},debugEnabled:{type:Boolean,default:!1},sendNonERPtoaiEnabled:{type:Boolean,default:!1}},emits:["toggleAutoRead","togglePollyPreference","toggleDebug","toggleSendNonERP"],setup(e){const t=e,n=Ee(()=>{var s,r;return(s=t.ttsConfig)!=null&&s.enableVoiceChat?(r=t.ttsConfig)!=null&&r.pollyAvailable?"Available":"Unavailable":"Voice disabled on server"});return(s,r)=>{var i,o,a,l,f,u,d,g,y,E,x,M,m,H,Y;return F(),W("div",Ep,[r[10]||(r[10]=w("div",{class:"chat-card motion-safe:animate-fade-rise rounded-xl p-4 text-slate-900"},[w("h3",{class:"text-sm font-semibold tracking-[0.01em]"},"Speech Settings"),w("p",{class:"mt-1 text-xs leading-relaxed text-slate-600"},"These controls apply only inside this chatbot box for the current browser session.")],-1)),w("div",Ap,[w("div",Rp,[r[4]||(r[4]=w("div",null,[w("p",{class:"text-sm font-semibold text-slate-900"},"Auto Read Replies"),w("p",{class:"mt-1 text-xs text-slate-600"},"Automatically read bot replies aloud.")],-1)),w("button",{class:we(["group relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200",e.autoReadEnabled?"bg-emerald-500/95":"bg-slate-300"]),"aria-pressed":e.autoReadEnabled?"true":"false",title:e.autoReadEnabled?"Disable auto read":"Enable auto read",onClick:r[0]||(r[0]=$=>s.$emit("toggleAutoRead"))},[w("span",{class:we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200",e.autoReadEnabled?"left-[1.45rem]":"left-0.5"])},null,2)],10,Cp)]),w("p",{class:we(["mt-3 text-[11px] font-medium",e.autoReadEnabled?"text-emerald-700":"text-slate-500"])},Je(e.autoReadEnabled?"Auto read is active.":"Auto read is currently off."),3)]),w("div",Pp,[w("div",Ip,[w("div",null,[r[5]||(r[5]=w("p",{class:"text-sm font-semibold text-slate-900"},"Use Amazon Polly",-1)),r[6]||(r[6]=w("p",{class:"mt-1 text-xs text-slate-600"},"Use Polly when available; otherwise browser speech is used automatically.",-1)),w("p",Mp,"Availability: "+Je(n.value),1),(i=e.settings)!=null&&i.aws_region?(F(),W("p",Op,"Region: "+Je(e.settings.aws_region),1)):qe("",!0),(o=e.ttsConfig)!=null&&o.voiceId?(F(),W("p",Lp,"Voice: "+Je(e.ttsConfig.voiceId),1)):qe("",!0)]),w("button",{class:we(["relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200 disabled:cursor-not-allowed disabled:opacity-55",(a=e.ttsConfig)!=null&&a.usePolly&&((l=e.ttsConfig)!=null&&l.enableVoiceChat)&&((f=e.ttsConfig)!=null&&f.pollyAvailable)?"bg-emerald-500/95":"bg-slate-300"]),"aria-pressed":(u=e.ttsConfig)!=null&&u.usePolly&&((d=e.ttsConfig)!=null&&d.enableVoiceChat)&&((g=e.ttsConfig)!=null&&g.pollyAvailable)?"true":"false",disabled:!((y=e.ttsConfig)!=null&&y.pollyAvailable)||!((E=e.ttsConfig)!=null&&E.enableVoiceChat),onClick:r[1]||(r[1]=$=>s.$emit("togglePollyPreference"))},[w("span",{class:we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200",(x=e.ttsConfig)!=null&&x.usePolly&&((M=e.ttsConfig)!=null&&M.enableVoiceChat)&&((m=e.ttsConfig)!=null&&m.pollyAvailable)?"left-[1.45rem]":"left-0.5"])},null,2)],10,$p)]),(H=e.ttsConfig)!=null&&H.enableVoiceChat?(Y=e.ttsConfig)!=null&&Y.pollyAvailable?qe("",!0):(F(),W("p",Np,"Polly is not available for this site. Browser speech will be used.")):(F(),W("p",Dp,"Voice chat is disabled in ChangAI Settings."))]),w("div",Fp,[w("div",Bp,[w("div",null,[r[7]||(r[7]=w("p",{class:"text-sm font-semibold text-slate-900"},"Enable Debug Tab",-1)),r[8]||(r[8]=w("p",{class:"mt-1 text-xs text-slate-600"}," Show or hide the Debug tab inside this chatbot. ",-1)),w("p",{class:we(["mt-2 text-[11px] font-medium",e.debugEnabled?"text-emerald-700":"text-slate-500"])},Je(e.debugEnabled?"Debug tab is active.":"Debug tab is currently off."),3)]),w("button",{type:"button",class:we(["relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200",e.debugEnabled?"bg-emerald-500/95":"bg-slate-300"]),"aria-pressed":e.debugEnabled?"true":"false",title:e.debugEnabled?"Disable debug tab":"Enable debug tab",onClick:r[2]||(r[2]=$=>s.$emit("toggleDebug"))},[w("span",{class:we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200",e.debugEnabled?"left-[1.45rem]":"left-0.5"])},null,2)],10,Hp)])]),w("div",zp,[w("div",Up,[r[9]||(r[9]=w("div",null,[w("p",{class:"text-sm font-semibold text-slate-900"},"Send non-ERP questions directly to AI"),w("p",{class:"mt-1 text-xs text-slate-600"},"Questions unrelated to your ERP will skip the system and go straight to AI")],-1)),w("button",{class:we(["group relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200",e.sendNonERPtoaiEnabled?"bg-emerald-500/95":"bg-slate-300"]),"aria-pressed":e.sendNonERPtoaiEnabled?"true":"false",title:e.sendNonERPtoaiEnabled?"Non-ERP questions are being sent directly to AI":"Enable direct AI reply for non-ERP questions",onClick:r[3]||(r[3]=$=>s.$emit("toggleSendNonERP"))},[w("span",{class:we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200",e.sendNonERPtoaiEnabled?"left-[1.45rem]":"left-0.5"])},null,2)],10,jp)]),w("p",{class:we(["mt-3 text-[11px] font-medium",e.sendNonERPtoaiEnabled?"text-emerald-700":"text-slate-500"])},Je(e.sendNonERPtoaiEnabled?"Non-ERP questions are now routed directly to AI":"Direct AI routing is currently off"),3)])])}}},qp={key:0,class:"pointer-events-none absolute -top-14 left-0 right-0 z-20 flex justify-center px-2",role:"status","aria-live":"polite"},Wp={__name:"StatusToast",props:{visible:{type:Boolean,required:!0},message:{type:String,default:""},type:{type:String,default:"info"},dismissible:{type:Boolean,default:!0}},emits:["close"],setup(e){const t=e,n=Ee(()=>t.type==="error"?"bg-red-50 text-red-700 ring-red-200":(t.type==="listening","bg-blue-50 text-blue-700 ring-blue-200")),s=Ee(()=>t.type==="error"?"bg-red-500":t.type==="listening"?"bg-blue-500 animate-pulse":"bg-blue-500");return(r,i)=>(F(),Et(Pu,{"enter-active-class":"transition duration-200 ease-out","enter-from-class":"translate-y-1 opacity-0","enter-to-class":"translate-y-0 opacity-100","leave-active-class":"transition duration-150 ease-in","leave-from-class":"translate-y-0 opacity-100","leave-to-class":"translate-y-1 opacity-0"},{default:Di(()=>[e.visible?(F(),W("div",qp,[w("div",{class:we(["pointer-events-auto flex max-w-[92%] items-start gap-2 rounded-lg px-3 py-2 text-xs shadow-lg ring-1",n.value])},[w("span",{class:we(["mt-0.5 h-2 w-2 shrink-0 rounded-full",s.value])},null,2),w("span",null,Je(e.message),1),e.dismissible?(F(),W("button",{key:0,type:"button",class:"ml-1 appearance-none border-0 text-current/80 transition hover:text-current focus:outline-none","aria-label":"Dismiss notification",onClick:i[0]||(i[0]=o=>r.$emit("close"))}," × ")):qe("",!0)],2)])):qe("",!0)]),_:1}))}},Gp={class:"relative w-full"},Kp=["placeholder","disabled"],Yp=["title","aria-label","disabled"],Zp={key:0,viewBox:"0 0 24 24",width:"16",height:"16",fill:"currentColor","aria-hidden":"true"},Xp={key:1,viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},Qp={key:2,viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true",class:"animate-spin"},Jp=["title","aria-label","disabled"],ed={key:0,viewBox:"0 0 24 24",width:"18",height:"18",fill:"none","aria-hidden":"true",class:"text-rose-600 motion-safe:animate-stop-button-pulse"},td={key:1,viewBox:"0 0 24 24",width:"16",height:"16",fill:"currentColor","aria-hidden":"true"},nd={__name:"ChatForm",props:{placeholder:{type:String,default:"Message..."},disabled:{type:Boolean,default:!1},isAwaitingResponse:{type:Boolean,default:!1}},emits:["submit","cancel"],setup(e,{expose:t,emit:n}){const s=e,r=n,i=J(""),o=J(null),a=J(!1),l=J(!1),f=J(!1),u=J(!1),d=J(!1),g=J(!1),y=J(!1),E=J(""),x=J("info"),M=J("Voice input is unavailable in this browser/context.");let m=null,H=null,Y=null;const $=J(""),z=J(!1),O=Ee(()=>f.value?"Requesting microphone permission...":u.value?"Starting voice input...":d.value?"Stopping voice input...":l.value?a.value?"Stop voice input":"Start voice input":"Voice input is unavailable in this browser/context"),P=Ee(()=>s.isAwaitingResponse?"Stop response":"Send"),ee=Ee(()=>s.isAwaitingResponse?!1:s.disabled||!i.value.trim()),V=Ee(()=>s.isAwaitingResponse?"bg-white border border-rose-100 shadow-[0_8px_20px_-12px_rgba(159,18,57,0.35)] hover:bg-rose-50":"bg-linear-to-br from-brand-500 to-brand-600 text-white shadow-[0_10px_24px_-16px_rgba(109,79,194,0.85)] hover:from-brand-600 hover:to-violet-700");function se(){return typeof window=="undefined"?null:window.SpeechRecognition||window.webkitSpeechRecognition||null}function fe(){var ae;const re=se(),B=typeof window!="undefined"?window.isSecureContext:!1,de=typeof navigator!="undefined"&&!!((ae=navigator.mediaDevices)!=null&&ae.getUserMedia);l.value=!!(re&&B&&de),B?(!de||!re)&&(M.value="Voice input is not supported in this browser."):M.value="Voice input requires HTTPS (or localhost).",re&&l.value&&(m=new re,m.continuous=!0,m.interimResults=!0,m.lang=typeof navigator!="undefined"&&navigator.language||"en-US",m.onstart=()=>{a.value=!0,u.value=!1,d.value=!1,D(),C("Listening... Tap mic to stop","listening",{persistent:!0,key:"listening"})},m.onend=()=>{a.value=!1,u.value=!1,d.value=!1,D(),$.value==="listening"&&pe(),z.value&&(z.value=!1,Ge())},m.onerror=ve=>{if(a.value=!1,u.value=!1,d.value=!1,D(),z.value=!1,(ve==null?void 0:ve.error)==="not-allowed"||(ve==null?void 0:ve.error)==="service-not-allowed"){C("Microphone permission denied. Please allow microphone access in browser settings.","error");return}if((ve==null?void 0:ve.error)==="audio-capture"){C("No microphone detected. Please connect a microphone and try again.","error");return}if((ve==null?void 0:ve.error)==="no-speech"){C("No speech detected. Try speaking a bit louder.","info");return}C("Voice input failed. Please try again.","error")},m.onresult=ve=>{let ge="";for(let nt=ve.resultIndex;nt{if(m&&!(!a.value&&!d.value))try{m.abort()}catch{d.value=!1,z.value=!1}},1200)}}function Se(){if(!l.value||!m){C(M.value,"error");return}if(a.value||u.value||d.value){Z({submitAfterStop:a.value});return}te()}async function Ue(){var re;if(g.value)return!0;if(!((re=navigator.mediaDevices)!=null&&re.getUserMedia))return C("Microphone API is unavailable in this browser.","error"),!1;f.value=!0,C("Requesting microphone permission...","info",{persistent:!0,key:"requesting"});try{return(await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(de=>de.stop()),g.value=!0,!0}catch(B){return(B==null?void 0:B.name)==="NotAllowedError"||(B==null?void 0:B.name)==="SecurityError"?C("Microphone permission denied. Please allow it and try again.","error"):(B==null?void 0:B.name)==="NotFoundError"?C("No microphone found on this device.","error"):C("Unable to access microphone. Please check browser permissions.","error"),!1}finally{f.value=!1,$.value==="requesting"&&pe()}}async function te(){var B;if(!(!m||a.value||u.value||d.value||!await Ue()||!m)){z.value=!1,(B=o.value)==null||B.focus(),u.value=!0;try{m.start()}catch(de){u.value=!1,d.value=!1,(de==null?void 0:de.name)!=="InvalidStateError"&&C("Unable to start voice input. Please try again.","error")}}}function C(re,B="info",de={}){const{duration:ae=4200,persistent:ve=!1,key:ge=""}=de;E.value=re,x.value=B,$.value=ge,y.value=!0,H&&clearTimeout(H),ve||(H=setTimeout(()=>{y.value=!1,$.value=""},ae))}function pe(){y.value=!1,$.value="",H&&(clearTimeout(H),H=null)}function Ge(){if(s.isAwaitingResponse){r("cancel");return}const re=i.value.trim();re&&((a.value||u.value||d.value)&&m&&Z({submitAfterStop:!1}),r("submit",re),i.value="")}return t({focus:()=>{var re;return(re=o.value)==null?void 0:re.focus()}}),zn(()=>{fe()}),ys(()=>{if(m&&(a.value||u.value||d.value)){z.value=!1,D();try{m.abort()}catch{}}D(),pe()}),(re,B)=>(F(),W("div",Gp,[w("form",{class:"group flex min-h-11 items-center gap-2 rounded-full border border-slate-200/90 bg-white/95 px-3 shadow-[0_12px_26px_-20px_rgba(15,23,42,0.7)] transition-all duration-250 focus-within:-translate-y-0.5 focus-within:border-brand-200 focus-within:shadow-[0_18px_30px_-20px_rgba(13,110,253,0.5)] focus-within:ring-2 focus-within:ring-brand-500/25",style:{"border-radius":"9999px"},autocomplete:"off",onSubmit:pt(Ge,["prevent"]),onClick:B[5]||(B[5]=pt(()=>{},["stop"])),onMousedown:B[6]||(B[6]=pt(()=>{},["stop"])),onKeydown:B[7]||(B[7]=pt(()=>{},["stop"])),onKeyup:B[8]||(B[8]=pt(()=>{},["stop"]))},[Pa(w("input",{ref_key:"inputRef",ref:o,type:"text","onUpdate:modelValue":B[0]||(B[0]=de=>i.value=de),class:"h-11 w-full border-none bg-transparent text-sm font-medium text-slate-800 placeholder:text-slate-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",placeholder:e.disabled?"Waiting for response...":e.placeholder,disabled:e.disabled,required:"",onKeydown:B[1]||(B[1]=pt(()=>{},["stop"])),onKeyup:B[2]||(B[2]=pt(()=>{},["stop"])),onKeypress:B[3]||(B[3]=pt(()=>{},["stop"])),onInput:B[4]||(B[4]=pt(()=>{},["stop"]))},null,40,Kp),[[Xu,i.value]]),w("button",{type:"button",class:we(["grid h-8 w-8 shrink-0 appearance-none place-items-center rounded-full border border-transparent text-slate-600 transition-all duration-200 hover:-translate-y-0.5 hover:border-slate-200 hover:bg-slate-100 hover:text-slate-900 focus:outline-none disabled:cursor-not-allowed disabled:opacity-40",a.value?"border-red-200 bg-red-100 text-red-600 shadow-[0_10px_20px_-18px_rgba(220,38,38,0.9)] hover:bg-red-100 hover:text-red-600":""]),style:{"border-radius":"9999px"},title:O.value,"aria-label":O.value,disabled:e.disabled||!l.value||f.value||u.value||d.value,onClick:Se},[a.value&&!f.value?(F(),W("svg",Zp,B[9]||(B[9]=[w("rect",{x:"6",y:"6",width:"12",height:"12",rx:"2"},null,-1)]))):f.value?(F(),W("svg",Qp,B[11]||(B[11]=[w("circle",{cx:"12",cy:"12",r:"9",opacity:"0.3"},null,-1),w("path",{d:"M21 12a9 9 0 0 1-9 9"},null,-1)]))):(F(),W("svg",Xp,B[10]||(B[10]=[w("path",{d:"M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z"},null,-1),w("path",{d:"M19 10v2a7 7 0 0 1-14 0v-2"},null,-1),w("path",{d:"M12 19v3"},null,-1)])))],10,Yp),w("button",{type:"submit",title:P.value,"aria-label":P.value,class:we(["grid h-8 w-8 shrink-0 appearance-none place-items-center rounded-full border-0 transition-all duration-200 hover:-translate-y-0.5 focus:outline-none disabled:cursor-not-allowed disabled:opacity-40",V.value]),style:{"border-radius":"9999px"},disabled:ee.value},[e.isAwaitingResponse?(F(),W("svg",ed,B[12]||(B[12]=[w("circle",{cx:"12",cy:"12",r:"8",stroke:"currentColor","stroke-width":"2.1",class:"opacity-95"},null,-1),w("rect",{x:"9",y:"9",width:"6",height:"6",rx:"1.35",fill:"currentColor"},null,-1)]))):(F(),W("svg",td,B[13]||(B[13]=[w("path",{d:"M4 12l1.41 1.41L11 7.83V20h2V7.83l5.59 5.58L20 12l-8-8-8 8z"},null,-1)])))],10,Jp)],32),Oe(Wp,{visible:y.value,message:E.value,type:x.value,dismissible:x.value!=="listening",onClose:pe},null,8,["visible","message","type","dismissible"])]))}},sd={class:"relative overflow-hidden bg-linear-to-br from-brand-600 via-brand-500 to-violet-400"},rd={class:"min-w-0"},id={key:1,class:"border-t border-slate-200/80 bg-white/90 px-3 py-3 pb-[calc(12px+env(safe-area-inset-bottom))] backdrop-blur-sm sm:px-4 sm:py-4"},od=56,ld={__name:"ChatbotPopup",props:{isOpen:{type:Boolean,required:!0},activeTab:{type:String,required:!0},debugEnabled:{type:Boolean,default:!1},sendNonERPtoaiEnabled:{type:Boolean,default:!1},chatHistory:{type:Array,required:!0},debugLogs:{type:Array,required:!0},currentDebug:{type:Object,default:null},supportHistory:{type:Array,required:!0},autoReadEnabled:{type:Boolean,required:!0},ttsConfig:{type:Object,required:!0},activeTtsProvider:{type:String,required:!0},settings:{type:Object,default:null},isAwaitingChatResponse:{type:Boolean,default:!1},isAwaitingSupportResponse:{type:Boolean,default:!1}},emits:["close","submit","cancelResponse","update:activeTab","toggleAutoRead","togglePollyPreference","toggleDebug","toggleSendNonERP"],setup(e,{expose:t,emit:n}){const s=e,r=n,i=J(null),o=J(null),a=J(s.activeTab),l=J("default"),f=J(!1),u=Ee(()=>a.value==="support"?s.isAwaitingSupportResponse:a.value==="chat"?s.isAwaitingChatResponse:!1);function d(){const M=i.value;if(!s.isOpen||!M){f.value=!1;return}const m=M.scrollHeight-M.clientHeight;if(m<=4){f.value=!1;return}const H=m-M.scrollTop;f.value=H>od}function g(){const M=i.value;M&&(M.scrollTo({top:M.scrollHeight,behavior:"smooth"}),setTimeout(()=>{d()},220))}function y(){St(()=>{d()})}function E(){if(l.value==="default"){l.value="half";return}if(l.value==="half"){l.value="full";return}l.value="default"}const x=Ee(()=>{const M="chat-shell fixed z-[9999] flex min-h-0 flex-col overflow-hidden border border-slate-200/80 shadow-[0_32px_80px_-44px_rgba(2,6,23,0.7),0_18px_40px_-24px_rgba(15,23,42,0.45)] transition-all duration-300 ease-out origin-bottom-right",m=s.isOpen?"pointer-events-auto opacity-100 translate-x-0 translate-y-0 scale-100 motion-safe:animate-surface-in":"pointer-events-none opacity-0 translate-x-1/5 translate-y-8 scale-95";return l.value==="full"?[M,m,"inset-0 h-screen w-screen max-h-screen max-w-screen rounded-none origin-center"]:l.value==="half"?[M,m,"bottom-[74px] right-5 h-[min(86vh,860px)] w-[min(50vw,860px)] rounded-2xl","max-[900px]:bottom-[78px] max-[900px]:right-3 max-[900px]:h-[min(86vh,760px)] max-[900px]:w-[min(70vw,760px)] max-[900px]:rounded-[14px]","max-[600px]:inset-0 max-[600px]:h-screen max-[600px]:w-screen max-[600px]:max-h-screen max-[600px]:max-w-screen max-[600px]:rounded-none max-[600px]:pb-[env(safe-area-inset-bottom)]"]:[M,m,"bottom-[74px] right-5 h-[min(560px,72vh)] w-[min(360px,calc(100vw-40px))] rounded-2xl","max-[900px]:bottom-[78px] max-[900px]:right-3 max-[900px]:h-[min(70vh,540px)] max-[900px]:w-[min(360px,calc(100vw-24px))] max-[900px]:rounded-[14px]","max-[600px]:inset-0 max-[600px]:h-screen max-[600px]:w-screen max-[600px]:max-h-screen max-[600px]:max-w-screen max-[600px]:rounded-none max-[600px]:pb-[env(safe-area-inset-bottom)]"]});return on(()=>s.activeTab,M=>{a.value=M,y()}),on(()=>s.isOpen,M=>{M&&a.value!=="settings"&&St(()=>{var m;return(m=o.value)==null?void 0:m.focus()}),y()}),on(a,M=>{r("update:activeTab",M),y()}),on(()=>[s.chatHistory.length,s.supportHistory.length,s.debugLogs.length,s.currentDebug],()=>{y()}),on(()=>s.debugEnabled,M=>{!M&&a.value==="debug"&&(a.value="chat"),y()}),zn(()=>{y()}),t({scrollToBottom(){St(()=>{g()})}}),(M,m)=>(F(),W("div",{class:we(x.value),onKeydown:m[9]||(m[9]=pt(()=>{},["stop"])),onKeyup:m[10]||(m[10]=pt(()=>{},["stop"])),onKeypress:m[11]||(m[11]=pt(()=>{},["stop"]))},[m[14]||(m[14]=w("div",{class:"pointer-events-none absolute -right-14 -top-14 h-36 w-36 rounded-full bg-brand-500/15 blur-2xl"},null,-1)),m[15]||(m[15]=w("div",{class:"pointer-events-none absolute -bottom-14 -left-12 h-32 w-32 rounded-full bg-violet-400/15 blur-2xl"},null,-1)),w("div",sd,[m[12]||(m[12]=w("div",{class:"pointer-events-none absolute inset-0 opacity-45",style:{background:"linear-gradient(120deg, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0.02) 52%, rgba(255,255,255,0.12) 100%)"}},null,-1)),Oe(vc,{windowMode:l.value,autoReadEnabled:e.autoReadEnabled,activeTtsProvider:e.activeTtsProvider,onClose:m[0]||(m[0]=H=>M.$emit("close")),onCycleResize:E,onToggleAutoRead:m[1]||(m[1]=H=>M.$emit("toggleAutoRead"))},null,8,["windowMode","autoReadEnabled","activeTtsProvider"]),Oe(Sc,{modelValue:a.value,"onUpdate:modelValue":m[2]||(m[2]=H=>a.value=H),debugEnabled:e.debugEnabled},null,8,["modelValue","debugEnabled"])]),w("div",{class:"chat-scrollbar min-h-0 flex-1 overflow-x-hidden overflow-y-scroll bg-slate-50/60 px-4 py-4 max-[900px]:px-3.5 max-[900px]:py-3.5 max-[600px]:px-3 max-[600px]:py-3",ref_key:"chatBodyRef",ref:i,onScrollPassive:d},[w("div",rd,[a.value==="chat"?(F(),Et(mp,{key:0,messages:e.chatHistory,autoReadEnabled:e.autoReadEnabled,ttsConfig:e.ttsConfig},null,8,["messages","autoReadEnabled","ttsConfig"])):a.value==="debug"&&e.debugEnabled?(F(),Et(_p,{key:1,logs:e.debugLogs,currentDebug:e.currentDebug},null,8,["logs","currentDebug"])):a.value==="support"?(F(),Et(Sp,{key:2,messages:e.supportHistory,autoReadEnabled:e.autoReadEnabled,ttsConfig:e.ttsConfig},null,8,["messages","autoReadEnabled","ttsConfig"])):a.value==="settings"?(F(),Et(Vp,{key:3,autoReadEnabled:e.autoReadEnabled,ttsConfig:e.ttsConfig,settings:e.settings,debugEnabled:e.debugEnabled,sendNonERPtoaiEnabled:e.sendNonERPtoaiEnabled,onToggleAutoRead:m[3]||(m[3]=H=>M.$emit("toggleAutoRead")),onTogglePollyPreference:m[4]||(m[4]=H=>M.$emit("togglePollyPreference")),onToggleDebug:m[5]||(m[5]=H=>M.$emit("toggleDebug")),onToggleSendNonERP:m[6]||(m[6]=H=>M.$emit("toggleSendNonERP"))},null,8,["autoReadEnabled","ttsConfig","settings","debugEnabled","sendNonERPtoaiEnabled"])):qe("",!0)])],544),f.value?(F(),W("button",{key:0,type:"button",class:we(["absolute right-4 z-20 grid h-9 w-9 place-items-center rounded-full border border-brand-200/70 bg-white/95 text-brand-600 shadow-[0_14px_26px_-16px_rgba(15,23,42,0.65)] transition-all duration-200 hover:-translate-y-0.5 hover:border-brand-300 hover:text-brand-700 focus:outline-none",a.value!=="settings"?"bottom-[calc(90px+env(safe-area-inset-bottom))] sm:bottom-[96px]":"bottom-4 sm:bottom-5"]),title:"Scroll to bottom","aria-label":"Scroll to bottom",onClick:g},m[13]||(m[13]=[w("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor","stroke-width":"2","aria-hidden":"true"},[w("path",{d:"M7 10l5 5 5-5"})],-1)]),2)):qe("",!0),a.value!=="settings"?(F(),W("div",id,[Oe(nd,{ref_key:"chatFormRef",ref:o,placeholder:a.value==="support"?"Message Support...":"Message...",disabled:u.value,isAwaitingResponse:u.value,onSubmit:m[7]||(m[7]=H=>M.$emit("submit",H)),onCancel:m[8]||(m[8]=H=>M.$emit("cancelResponse"))},null,8,["placeholder","disabled","isAwaitingResponse"])])):qe("",!0)],34))}},_l="changai_chat_id",Tl="changai_polly_enabled";function ad(){let e=sessionStorage.getItem(_l);return e||(e=`session_${Date.now()}_${crypto.randomUUID()}`,sessionStorage.setItem(_l,e)),e}function ud(){const e=localStorage.getItem(Tl);return e===null?!0:e==="true"}function cd(e){localStorage.setItem(Tl,String(!!e))}const fd={__name:"App",setup(e){const t=J(!1),n=J("chat"),s=J([]),r=J([]),i=J(!1),o=J([]),a=J(null),l=J("actual"),f=J(!0),u=J(null),d=J(!1),g=J(null),y=J(!1),E=J({enableVoiceChat:!1,pollyAvailable:!1,usePolly:!0,voiceId:"Zayd",enable_changai:!1}),x=J("off"),M=J(null),m=J(null),H=Ee(()=>M.value!==null),Y=Ee(()=>m.value!==null);function $(){if(!E.value.enableVoiceChat){x.value="off";return}x.value=E.value.usePolly?"polly":"browser"}function z(te){var pe;const C=(pe=te==null?void 0:te.detail)==null?void 0:pe.provider;(C==="polly"||C==="browser"||C==="off")&&(x.value=C)}async function O(){var pe,Ge,re,B,de;if(!(d.value||u.value)){d.value=!0;try{u.value=await Mc(l.value),E.value={enableVoiceChat:!!((pe=u.value)!=null&&pe.enable_voice_chat),pollyAvailable:!!((Ge=u.value)!=null&&Ge.polly_enabled),usePolly:!!((re=u.value)!=null&&re.polly_enabled)&&ud(),voiceId:((B=u.value)==null?void 0:B.polly_voice_id)||"Zayd",enable_changai:!!((de=u.value)!=null&&de.enable_changai)},$(),r.value.push({type:"settings",settings:u.value})}catch(ae){const ve=vl(ae);r.value.push({type:"settings",error:ve})}finally{d.value=!1}}}function P(){t.value=!t.value}function ee(){var te;(te=a.value)==null||te.scrollToBottom()}function V(){f.value=!f.value}function se(){const te=!E.value.usePolly;E.value={...E.value,usePolly:te&&E.value.pollyAvailable},cd(E.value.usePolly),$()}function fe(){y.value=!y.value}async function D(te){n.value==="support"?await Ue(te):await Z(te)}async function Z(te){var ye,ht;g.value=null,l.value==="actual"&&await O(),s.value.push({role:"user",text:te}),await St(),ee();const C=cs({role:"model",text:"Thinking...",cancelable:!0,isStatus:!0,statusType:"thinking"});s.value.push(C),await St(),ee();let pe=!1;const Ge=ad(),re=`${Ge}_${Date.now()}`,B=y.value,de=Pc(te,Ge,l.value,re,y.value),ae=`debug_${re}`;let ve=Date.now();const ge=[],nt=G=>{var h;const Ce=Date.now(),Ft=((Ce-ve)/1e3).toFixed(2);ve=Ce;const c=`${G.message} (${Ft}s)`;if(G.message&&(ge.push(c),g.value=c),!G.done&&G.message&&(C.text=G.message,C.statusType="pipeline"),G.done){C.cancelable=!1,G.error?(C.text=`⚠️ ${G.message||"Something failed"}`,C.isStatus=!1,C.statusType=null):(h=G.data)!=null&&h.answer&&(C.text=G.data.answer,C.isStatus=!1,C.statusType=null),frappe.realtime.off(ae),g.value=null;return}};frappe.realtime.on(ae,nt),M.value=()=>{pe||(pe=!0,de.cancel(),frappe.realtime.off(ae),C.isStatus=!1,C.statusType=null,C.text="Cancelled by user.",r.value.push({type:"cancelled",user:te,steps:[...ge]}),g.value=null,C.cancelable=!1,M.value=null)};try{const G=await de.promise;if(G!=null&&G.open_report){if(C.isStatus=!1,C.statusType=null,C.text=`Opening "${G.report_name}" report." `,r.value.push({type:"success",steps:[...ge],final_response:G,entity_raw:G.entity_raw}),g.value=null,!G.report_name){C.text="Report name extraction failed.Can you ask the same question again?";return}frappe.set_route("query-report",G.report_name,G.filters||{});return}else if(G!=null&&G.create_entity){C.isStatus=!1,C.statusType=null,C.cancelable=!1,C.text=`Opening "${G.doc}" doctype for creating Entity "${G.entity_name}" record.`,r.value.push({type:"success",user:te,steps:[...ge],final_response:G}),g.value=null;const Ft=G.doc,c=G.entity_name||"",b={Customer:{customer_name:c},Supplier:{supplier_name:c},Employee:{employee_name:c},Item:{item_code:c,item_name:c},Project:{project_name:c},Lead:{lead_name:c},Opportunity:{opportunity_name:c}}[Ft]||{};frappe.route_options=b,frappe.set_route("Form",Ft,"new");const k=setInterval(()=>{cur_frm&&cur_frm.doctype===Ft&&cur_frm.is_new()&&(clearInterval(k),Object.entries(b).forEach(([v,_])=>{_&&cur_frm.fields_dict[v]&&(cur_frm.set_value(v,_),cur_frm.refresh_field(v))}))},200);return}if(pe)return;C.cancelable=!1;const Ce=((ye=bp(G==null?void 0:G.Bot))==null?void 0:ye.trim())||"No response.";C.isStatus=!1,C.statusType=null,C.text=Ce,r.value.push({type:"success",user:te,steps:[...ge],final_response:G}),g.value=null}catch(G){if(pe)return;frappe.realtime.off(ae),C.cancelable=!1,C.isStatus=!1,C.statusType=null;const Ce=vl(G);g.value=null,r.value.push({type:"failed",user:te,steps:[...ge],error:Ce}),(G==null?void 0:G.code)==="ERR_NETWORK_CHANGED"||(ht=G==null?void 0:G.message)!=null&&ht.includes("ERR_NETWORK_CHANGED")?(C.isStatus=!1,C.statusType=null,C.text="⚠️ Network error. Please check your connection and try again."):(C.isStatus=!1,C.statusType=null,C.text="⚠️ Something went wrong. Please try again.")}finally{frappe.realtime.off(ae),pe||(M.value=null)}await St(),ee()}function Se(){var te,C;if(n.value==="support"){(te=m.value)==null||te.call(m);return}(C=M.value)==null||C.call(M)}async function Ue(te){o.value.push({role:"user",text:te}),await St(),ee();const C=cs({role:"model",text:"Sending to support...",isStatus:!0,statusType:"support"});o.value.push(C),await St(),ee();let pe=!1;const Ge=Ic(te,l.value);m.value=()=>{pe||(pe=!0,Ge.cancel(),C.isStatus=!1,C.statusType=null,C.text="Cancelled by user.",m.value=null)};try{const re=await Ge.promise;if(pe)return;C.isStatus=!1,C.statusType=null,C.text=re?wl(re):"Support request sent successfully."}catch{if(pe)return;C.isStatus=!1,C.statusType=null,C.text="⚠️ Failed to reach support. Please try again."}finally{pe||(m.value=null)}await St(),ee()}return zn(()=>{typeof window!="undefined"&&window.addEventListener("changai-tts-provider",z),l.value==="actual"&&O()}),ys(()=>{typeof window!="undefined"&&window.removeEventListener("changai-tts-provider",z)}),(te,C)=>(F(),W(He,null,[E.value.enable_changai?(F(),Et(ac,{key:0,isOpen:t.value,onToggle:P},null,8,["isOpen"])):qe("",!0),Oe(ld,{ref_key:"popupRef",ref:a,isOpen:t.value,activeTab:n.value,"onUpdate:activeTab":C[0]||(C[0]=pe=>n.value=pe),chatHistory:s.value,debugLogs:r.value,currentDebug:g.value,supportHistory:o.value,autoReadEnabled:f.value,ttsConfig:E.value,activeTtsProvider:x.value,settings:u.value,isAwaitingChatResponse:H.value,isAwaitingSupportResponse:Y.value,debugEnabled:i.value,sendNonERPtoaiEnabled:y.value,onToggleDebug:C[1]||(C[1]=pe=>i.value=!i.value),onClose:C[2]||(C[2]=pe=>t.value=!1),onSubmit:D,onCancelResponse:Se,onToggleAutoRead:V,onTogglePollyPreference:se,onToggleSendNonERP:fe},null,8,["isOpen","activeTab","chatHistory","debugLogs","currentDebug","supportHistory","autoReadEnabled","ttsConfig","activeTtsProvider","settings","isAwaitingChatResponse","isAwaitingSupportResponse","debugEnabled","sendNonERPtoaiEnabled"])],64))}};function pd(){const e=document.querySelector('link[href*="/assets/changai/dist/changai-chatbot.css"]');if(e!=null&&e.href)return e.href;const t=Array.from(document.scripts).find(n=>{var s;return(s=n.src)==null?void 0:s.includes("/assets/changai/dist/changai-chatbot.js")});return t!=null&&t.src?t.src.replace(/changai-chatbot\.js(\?.*)?$/,"changai-chatbot.css$1"):null}function dd(e){const t=pd();if(!t){const n=Array.from(document.querySelectorAll("style[data-vite-dev-id]"));return n.length&&n.forEach(s=>{const r=document.createElement("style");r.dataset.changaiShadowDevStyle="1",r.textContent=s.textContent||"",e.appendChild(r)}),Promise.resolve()}return e.querySelector('link[data-changai-shadow-style="1"]')?Promise.resolve():new Promise(n=>{const s=document.createElement("link");s.rel="stylesheet",s.href=t,s.dataset.changaiShadowStyle="1",s.onload=()=>n(),s.onerror=()=>n(),e.appendChild(s),setTimeout(n,1200)})}async function kl(){if(document.getElementById("changai-chatbot-host"))return;const e=document.createElement("div");e.id="changai-chatbot-host",document.body.appendChild(e);const t=e.attachShadow({mode:"open"});await dd(t);const n=document.createElement("div");n.id="changai-chatbot-root",t.appendChild(n),nc(fd).mount(n);function s(r){r.stopPropagation()}n.addEventListener("keydown",s),n.addEventListener("keyup",s),n.addEventListener("keypress",s)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",kl):kl()})(); +`} tablecell(e) { + let t = this.parser.parseInline(e.tokens), n = e.header ? "th" : "td"; return (e.align ? `<${n} align="${e.align}">` : `<${n}>`) + t + ` +`} strong({ tokens: e }) { return `${this.parser.parseInline(e)}` } em({ tokens: e }) { return `${this.parser.parseInline(e)}` } codespan({ text: e }) { return `${Ct(e, !0)}` } br(e) { return "
    " } del({ tokens: e }) { return `${this.parser.parseInline(e)}` } link({ href: e, title: t, tokens: n }) { let s = this.parser.parseInline(n), r = Jo(e); if (r === null) return s; e = r; let i = '
    ", i } image({ href: e, title: t, text: n, tokens: s }) { s && (n = this.parser.parseInline(s, this.parser.textRenderer)); let r = Jo(e); if (r === null) return Ct(n); e = r; let i = `${Ct(n)} { let l = o[a].flat(1 / 0); n = n.concat(this.walkTokens(l, t)) }) : o.tokens && (n = n.concat(this.walkTokens(o.tokens, t))) } }return n } use(...e) { let t = this.defaults.extensions || { renderers: {}, childTokens: {} }; return e.forEach(n => { let s = { ...n }; if (s.async = this.defaults.async || s.async || !1, n.extensions && (n.extensions.forEach(r => { if (!r.name) throw new Error("extension name required"); if ("renderer" in r) { let i = t.renderers[r.name]; i ? t.renderers[r.name] = function (...o) { let a = r.renderer.apply(this, o); return a === !1 && (a = i.apply(this, o)), a } : t.renderers[r.name] = r.renderer } if ("tokenizer" in r) { if (!r.level || r.level !== "block" && r.level !== "inline") throw new Error("extension level must be 'block' or 'inline'"); let i = t[r.level]; i ? i.unshift(r.tokenizer) : t[r.level] = [r.tokenizer], r.start && (r.level === "block" ? t.startBlock ? t.startBlock.push(r.start) : t.startBlock = [r.start] : r.level === "inline" && (t.startInline ? t.startInline.push(r.start) : t.startInline = [r.start])) } "childTokens" in r && r.childTokens && (t.childTokens[r.name] = r.childTokens) }), s.extensions = t), n.renderer) { let r = this.defaults.renderer || new $s(this.defaults); for (let i in n.renderer) { if (!(i in r)) throw new Error(`renderer '${i}' does not exist`); if (["options", "parser"].includes(i)) continue; let o = i, a = n.renderer[o], l = r[o]; r[o] = (...f) => { let u = a.apply(r, f); return u === !1 && (u = l.apply(r, f)), u || "" } } s.renderer = r } if (n.tokenizer) { let r = this.defaults.tokenizer || new Ls(this.defaults); for (let i in n.tokenizer) { if (!(i in r)) throw new Error(`tokenizer '${i}' does not exist`); if (["options", "rules", "lexer"].includes(i)) continue; let o = i, a = n.tokenizer[o], l = r[o]; r[o] = (...f) => { let u = a.apply(r, f); return u === !1 && (u = l.apply(r, f)), u } } s.tokenizer = r } if (n.hooks) { let r = this.defaults.hooks || new Zn; for (let i in n.hooks) { if (!(i in r)) throw new Error(`hook '${i}' does not exist`); if (["options", "block"].includes(i)) continue; let o = i, a = n.hooks[o], l = r[o]; Zn.passThroughHooks.has(i) ? r[o] = f => { if (this.defaults.async && Zn.passThroughHooksRespectAsync.has(i)) return (async () => { let d = await a.call(r, f); return l.call(r, d) })(); let u = a.call(r, f); return l.call(r, u) } : r[o] = (...f) => { if (this.defaults.async) return (async () => { let d = await a.apply(r, f); return d === !1 && (d = await l.apply(r, f)), d })(); let u = a.apply(r, f); return u === !1 && (u = l.apply(r, f)), u } } s.hooks = r } if (n.walkTokens) { let r = this.defaults.walkTokens, i = n.walkTokens; s.walkTokens = function (o) { let a = []; return a.push(i.call(this, o)), r && (a = a.concat(r.call(this, o))), a } } this.defaults = { ...this.defaults, ...s } }), this } setOptions(e) { return this.defaults = { ...this.defaults, ...e }, this } lexer(e, t) { return yt.lex(e, t != null ? t : this.defaults) } parser(e, t) { return wt.parse(e, t != null ? t : this.defaults) } parseMarkdown(e) { return (t, n) => { let s = { ...n }, r = { ...this.defaults, ...s }, i = this.onError(!!r.silent, !!r.async); if (this.defaults.async === !0 && s.async === !1) return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.")); if (typeof t > "u" || t === null) return i(new Error("marked(): input parameter is undefined or null")); if (typeof t != "string") return i(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(t) + ", string expected")); if (r.hooks && (r.hooks.options = r, r.hooks.block = e), r.async) return (async () => { let o = r.hooks ? await r.hooks.preprocess(t) : t, a = await (r.hooks ? await r.hooks.provideLexer(e) : e ? yt.lex : yt.lexInline)(o, r), l = r.hooks ? await r.hooks.processAllTokens(a) : a; r.walkTokens && await Promise.all(this.walkTokens(l, r.walkTokens)); let f = await (r.hooks ? await r.hooks.provideParser(e) : e ? wt.parse : wt.parseInline)(l, r); return r.hooks ? await r.hooks.postprocess(f) : f })().catch(i); try { r.hooks && (t = r.hooks.preprocess(t)); let o = (r.hooks ? r.hooks.provideLexer(e) : e ? yt.lex : yt.lexInline)(t, r); r.hooks && (o = r.hooks.processAllTokens(o)), r.walkTokens && this.walkTokens(o, r.walkTokens); let a = (r.hooks ? r.hooks.provideParser(e) : e ? wt.parse : wt.parseInline)(o, r); return r.hooks && (a = r.hooks.postprocess(a)), a } catch (o) { return i(o) } } } onError(e, t) { + return n => { + if (n.message += ` +Please report this to https://github.com/markedjs/marked.`, e) { let s = "

    An error occurred:

    " + Ct(n.message + "", !0) + "
    "; return t ? Promise.resolve(s) : s } if (t) return Promise.reject(n); throw n + } + } + }, pn = new vf; function xe(e, t) { return pn.parse(e, t) } xe.options = xe.setOptions = function (e) { return pn.setOptions(e), xe.defaults = pn.defaults, Ho(xe.defaults), xe }, xe.getDefaults = Sr, xe.defaults = cn, xe.use = function (...e) { return pn.use(...e), xe.defaults = pn.defaults, Ho(xe.defaults), xe }, xe.walkTokens = function (e, t) { return pn.walkTokens(e, t) }, xe.parseInline = pn.parseInline, xe.Parser = wt, xe.parser = wt.parse, xe.Renderer = $s, xe.TextRenderer = Lr, xe.Lexer = yt, xe.lexer = yt.lex, xe.Tokenizer = Ls, xe.Hooks = Zn, xe.parse = xe, xe.options, xe.setOptions, xe.use, xe.walkTokens, xe.parseInline, wt.parse, yt.lex; function sl(e, t) { (t == null || t > e.length) && (t = e.length); for (var n = 0, s = Array(t); n < t; n++)s[n] = e[n]; return s } function _f(e) { if (Array.isArray(e)) return e } function Tf(e, t) { var n = e == null ? null : typeof Symbol != "undefined" && e[Symbol.iterator] || e["@@iterator"]; if (n != null) { var s, r, i, o, a = [], l = !0, f = !1; try { if (i = (n = n.call(e)).next, t !== 0) for (; !(l = (s = i.call(n)).done) && (a.push(s.value), a.length !== t); l = !0); } catch (u) { f = !0, r = u } finally { try { if (!l && n.return != null && (o = n.return(), Object(o) !== o)) return } finally { if (f) throw r } } return a } } function kf() { + throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`) + } function Sf(e, t) { return _f(e) || Tf(e, t) || Ef(e, t) || kf() } function Ef(e, t) { if (e) { if (typeof e == "string") return sl(e, t); var n = {}.toString.call(e).slice(8, -1); return n === "Object" && e.constructor && (n = e.constructor.name), n === "Map" || n === "Set" ? Array.from(e) : n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n) ? sl(e, t) : void 0 } } const rl = Object.entries, il = Object.setPrototypeOf, Af = Object.isFrozen, Rf = Object.getPrototypeOf, Cf = Object.getOwnPropertyDescriptor; let Ze = Object.freeze, dt = Object.seal, Tn = Object.create, ol = typeof Reflect != "undefined" && Reflect, $r = ol.apply, Dr = ol.construct; Ze || (Ze = function (t) { return t }), dt || (dt = function (t) { return t }), $r || ($r = function (t, n) { for (var s = arguments.length, r = new Array(s > 2 ? s - 2 : 0), i = 2; i < s; i++)r[i - 2] = arguments[i]; return t.apply(n, r) }), Dr || (Dr = function (t) { for (var n = arguments.length, s = new Array(n > 1 ? n - 1 : 0), r = 1; r < n; r++)s[r - 1] = arguments[r]; return new t(...s) }); const kn = Ie(Array.prototype.forEach), Pf = Ie(Array.prototype.lastIndexOf), ll = Ie(Array.prototype.pop), Sn = Ie(Array.prototype.push), If = Ie(Array.prototype.splice), Xe = Array.isArray, Xn = Ie(String.prototype.toLowerCase), Nr = Ie(String.prototype.toString), al = Ie(String.prototype.match), En = Ie(String.prototype.replace), ul = Ie(String.prototype.indexOf), Mf = Ie(String.prototype.trim), Of = Ie(Number.prototype.toString), Lf = Ie(Boolean.prototype.toString), cl = typeof BigInt == "undefined" ? null : Ie(BigInt.prototype.toString), fl = typeof Symbol == "undefined" ? null : Ie(Symbol.prototype.toString), Ae = Ie(Object.prototype.hasOwnProperty), Qn = Ie(Object.prototype.toString), ze = Ie(RegExp.prototype.test), Ds = $f(TypeError); function Ie(e) { return function (t) { t instanceof RegExp && (t.lastIndex = 0); for (var n = arguments.length, s = new Array(n > 1 ? n - 1 : 0), r = 1; r < n; r++)s[r - 1] = arguments[r]; return $r(e, t, s) } } function $f(e) { return function () { for (var t = arguments.length, n = new Array(t), s = 0; s < t; s++)n[s] = arguments[s]; return Dr(e, n) } } function X(e, t) { let n = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : Xn; if (il && il(e, null), !Xe(t)) return e; let s = t.length; for (; s--;) { let r = t[s]; if (typeof r == "string") { const i = n(r); i !== r && (Af(t) || (t[s] = i), r = i) } e[r] = !0 } return e } function Df(e) { for (let t = 0; t < e.length; t++)Ae(e, t) || (e[t] = null); return e } function tt(e) { const t = Tn(null); for (const s of rl(e)) { var n = Sf(s, 2); const r = n[0], i = n[1]; Ae(e, r) && (Xe(i) ? t[r] = Df(i) : i && typeof i == "object" && i.constructor === Object ? t[r] = tt(i) : t[r] = i) } return t } function Nf(e) { switch (typeof e) { case "string": return e; case "number": return Of(e); case "boolean": return Lf(e); case "bigint": return cl ? cl(e) : "0"; case "symbol": return fl ? fl(e) : "Symbol()"; case "undefined": return Qn(e); case "function": case "object": { if (e === null) return Qn(e); const t = e, n = An(t, "toString"); if (typeof n == "function") { const s = n(t); return typeof s == "string" ? s : Qn(s) } return Qn(e) } default: return Qn(e) } } function An(e, t) { for (; e !== null;) { const s = Cf(e, t); if (s) { if (s.get) return Ie(s.get); if (typeof s.value == "function") return Ie(s.value) } e = Rf(e) } function n() { return null } return n } function Ff(e) { try { return ze(e, ""), !0 } catch { return !1 } } const pl = Ze(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]), Fr = Ze(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]), Br = Ze(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]), Bf = Ze(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]), Hr = Ze(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]), Hf = Ze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]), dl = Ze(["#text"]), hl = Ze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]), zr = Ze(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]), gl = Ze(["accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]), Ns = Ze(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]), zf = dt(/{{[\w\W]*|^[\w\W]*}}/g), Uf = dt(/<%[\w\W]*|^[\w\W]*%>/g), jf = dt(/\${[\w\W]*/g), Vf = dt(/^data-[\-\w.\u00B7-\uFFFF]+$/), qf = dt(/^aria-[\-\w]+$/), ml = dt(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i), Wf = dt(/^(?:\w+script|data):/i), Gf = dt(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g), Kf = dt(/^html$/i), Yf = dt(/^[a-z][.\w]*(-[.\w]+)+$/i), Rn = { element: 1, text: 3, progressingInstruction: 7, comment: 8, document: 9 }, Zf = function () { return typeof window == "undefined" ? null : window }, Xf = function (t, n) { if (typeof t != "object" || typeof t.createPolicy != "function") return null; let s = null; const r = "data-tt-policy-suffix"; n && n.hasAttribute(r) && (s = n.getAttribute(r)); const i = "dompurify" + (s ? "#" + s : ""); try { return t.createPolicy(i, { createHTML(o) { return o }, createScriptURL(o) { return o } }) } catch { return null } }, bl = function () { return { afterSanitizeAttributes: [], afterSanitizeElements: [], afterSanitizeShadowDOM: [], beforeSanitizeAttributes: [], beforeSanitizeElements: [], beforeSanitizeShadowDOM: [], uponSanitizeAttribute: [], uponSanitizeElement: [], uponSanitizeShadowNode: [] } }; function xl() { + let e = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : Zf(); const t = q => xl(q); if (t.version = "3.4.3", t.removed = [], !e || !e.document || e.document.nodeType !== Rn.document || !e.Element) return t.isSupported = !1, t; let n = e.document; const s = n, r = s.currentScript, i = e.DocumentFragment, o = e.HTMLTemplateElement, a = e.Node, l = e.Element, f = e.NodeFilter, u = e.NamedNodeMap, d = u === void 0 ? e.NamedNodeMap || e.MozNamedAttrMap : u, g = e.HTMLFormElement, y = e.DOMParser, E = e.trustedTypes, x = l.prototype, M = An(x, "cloneNode"), m = An(x, "remove"), H = An(x, "nextSibling"), Y = An(x, "childNodes"), $ = An(x, "parentNode"); if (typeof o == "function") { const q = n.createElement("template"); q.content && q.content.ownerDocument && (n = q.content.ownerDocument) } let z, O = ""; const P = n, ee = P.implementation, V = P.createNodeIterator, se = P.createDocumentFragment, fe = P.getElementsByTagName, D = s.importNode; let Z = bl(); t.isSupported = typeof rl == "function" && typeof $ == "function" && ee && ee.createHTMLDocument !== void 0; const Se = zf, Ue = Uf, te = jf, C = Vf, pe = qf, Ge = Wf, re = Gf, B = Yf; let de = ml, ae = null; const ve = X({}, [...pl, ...Fr, ...Br, ...Hr, ...dl]); let ge = null; const nt = X({}, [...hl, ...zr, ...gl, ...Ns]); let ye = Object.seal(Tn(null, { tagNameCheck: { writable: !0, configurable: !1, enumerable: !0, value: null }, attributeNameCheck: { writable: !0, configurable: !1, enumerable: !0, value: null }, allowCustomizedBuiltInElements: { writable: !0, configurable: !1, enumerable: !0, value: !1 } })), ht = null, G = null; const Ce = Object.seal(Tn(null, { tagCheck: { writable: !0, configurable: !1, enumerable: !0, value: null }, attributeCheck: { writable: !0, configurable: !1, enumerable: !0, value: null } })); let Ft = !0, c = !0, h = !1, b = !0, k = !1, v = !0, _ = !1, I = !1, R = !1, A = !1, S = !1, U = !1, L = !0, j = !1; const K = "user-content-"; let ne = !0, he = !1, ie = {}, ke = null; const $e = X({}, ["annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "style", "svg", "template", "thead", "title", "video", "xmp"]); let ot = null; const lt = X({}, ["audio", "video", "img", "source", "image", "track"]); let Bt = null; const Jn = X({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]), De = "http://www.w3.org/1998/Math/MathML", Qe = "http://www.w3.org/2000/svg", at = "http://www.w3.org/1999/xhtml"; let Ht = at, Ur = !1, jr = null; const hd = X({}, [De, Qe, at], Nr); let Vr = X({}, ["mi", "mo", "mn", "ms", "mtext"]), qr = X({}, ["annotation-xml"]); const gd = X({}, ["title", "style", "font", "a", "script"]); let es = null; const md = ["application/xhtml+xml", "text/html"], bd = "text/html"; let Le = null, Cn = null; const xd = n.createElement("form"), Sl = function (p) { return p instanceof RegExp || p instanceof Function }, Wr = function () { let p = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; if (Cn && Cn === p) return; (!p || typeof p != "object") && (p = {}), p = tt(p), es = md.indexOf(p.PARSER_MEDIA_TYPE) === -1 ? bd : p.PARSER_MEDIA_TYPE, Le = es === "application/xhtml+xml" ? Nr : Xn, ae = Ae(p, "ALLOWED_TAGS") && Xe(p.ALLOWED_TAGS) ? X({}, p.ALLOWED_TAGS, Le) : ve, ge = Ae(p, "ALLOWED_ATTR") && Xe(p.ALLOWED_ATTR) ? X({}, p.ALLOWED_ATTR, Le) : nt, jr = Ae(p, "ALLOWED_NAMESPACES") && Xe(p.ALLOWED_NAMESPACES) ? X({}, p.ALLOWED_NAMESPACES, Nr) : hd, Bt = Ae(p, "ADD_URI_SAFE_ATTR") && Xe(p.ADD_URI_SAFE_ATTR) ? X(tt(Jn), p.ADD_URI_SAFE_ATTR, Le) : Jn, ot = Ae(p, "ADD_DATA_URI_TAGS") && Xe(p.ADD_DATA_URI_TAGS) ? X(tt(lt), p.ADD_DATA_URI_TAGS, Le) : lt, ke = Ae(p, "FORBID_CONTENTS") && Xe(p.FORBID_CONTENTS) ? X({}, p.FORBID_CONTENTS, Le) : $e, ht = Ae(p, "FORBID_TAGS") && Xe(p.FORBID_TAGS) ? X({}, p.FORBID_TAGS, Le) : tt({}), G = Ae(p, "FORBID_ATTR") && Xe(p.FORBID_ATTR) ? X({}, p.FORBID_ATTR, Le) : tt({}), ie = Ae(p, "USE_PROFILES") ? p.USE_PROFILES && typeof p.USE_PROFILES == "object" ? tt(p.USE_PROFILES) : p.USE_PROFILES : !1, Ft = p.ALLOW_ARIA_ATTR !== !1, c = p.ALLOW_DATA_ATTR !== !1, h = p.ALLOW_UNKNOWN_PROTOCOLS || !1, b = p.ALLOW_SELF_CLOSE_IN_ATTR !== !1, k = p.SAFE_FOR_TEMPLATES || !1, v = p.SAFE_FOR_XML !== !1, _ = p.WHOLE_DOCUMENT || !1, A = p.RETURN_DOM || !1, S = p.RETURN_DOM_FRAGMENT || !1, U = p.RETURN_TRUSTED_TYPE || !1, R = p.FORCE_BODY || !1, L = p.SANITIZE_DOM !== !1, j = p.SANITIZE_NAMED_PROPS || !1, ne = p.KEEP_CONTENT !== !1, he = p.IN_PLACE || !1, de = Ff(p.ALLOWED_URI_REGEXP) ? p.ALLOWED_URI_REGEXP : ml, Ht = typeof p.NAMESPACE == "string" ? p.NAMESPACE : at, Vr = Ae(p, "MATHML_TEXT_INTEGRATION_POINTS") && p.MATHML_TEXT_INTEGRATION_POINTS && typeof p.MATHML_TEXT_INTEGRATION_POINTS == "object" ? tt(p.MATHML_TEXT_INTEGRATION_POINTS) : X({}, ["mi", "mo", "mn", "ms", "mtext"]), qr = Ae(p, "HTML_INTEGRATION_POINTS") && p.HTML_INTEGRATION_POINTS && typeof p.HTML_INTEGRATION_POINTS == "object" ? tt(p.HTML_INTEGRATION_POINTS) : X({}, ["annotation-xml"]); const T = Ae(p, "CUSTOM_ELEMENT_HANDLING") && p.CUSTOM_ELEMENT_HANDLING && typeof p.CUSTOM_ELEMENT_HANDLING == "object" ? tt(p.CUSTOM_ELEMENT_HANDLING) : Tn(null); if (ye = Tn(null), Ae(T, "tagNameCheck") && Sl(T.tagNameCheck) && (ye.tagNameCheck = T.tagNameCheck), Ae(T, "attributeNameCheck") && Sl(T.attributeNameCheck) && (ye.attributeNameCheck = T.attributeNameCheck), Ae(T, "allowCustomizedBuiltInElements") && typeof T.allowCustomizedBuiltInElements == "boolean" && (ye.allowCustomizedBuiltInElements = T.allowCustomizedBuiltInElements), k && (c = !1), S && (A = !0), ie && (ae = X({}, dl), ge = Tn(null), ie.html === !0 && (X(ae, pl), X(ge, hl)), ie.svg === !0 && (X(ae, Fr), X(ge, zr), X(ge, Ns)), ie.svgFilters === !0 && (X(ae, Br), X(ge, zr), X(ge, Ns)), ie.mathMl === !0 && (X(ae, Hr), X(ge, gl), X(ge, Ns))), Ce.tagCheck = null, Ce.attributeCheck = null, Ae(p, "ADD_TAGS") && (typeof p.ADD_TAGS == "function" ? Ce.tagCheck = p.ADD_TAGS : Xe(p.ADD_TAGS) && (ae === ve && (ae = tt(ae)), X(ae, p.ADD_TAGS, Le))), Ae(p, "ADD_ATTR") && (typeof p.ADD_ATTR == "function" ? Ce.attributeCheck = p.ADD_ATTR : Xe(p.ADD_ATTR) && (ge === nt && (ge = tt(ge)), X(ge, p.ADD_ATTR, Le))), Ae(p, "ADD_URI_SAFE_ATTR") && Xe(p.ADD_URI_SAFE_ATTR) && X(Bt, p.ADD_URI_SAFE_ATTR, Le), Ae(p, "FORBID_CONTENTS") && Xe(p.FORBID_CONTENTS) && (ke === $e && (ke = tt(ke)), X(ke, p.FORBID_CONTENTS, Le)), Ae(p, "ADD_FORBID_CONTENTS") && Xe(p.ADD_FORBID_CONTENTS) && (ke === $e && (ke = tt(ke)), X(ke, p.ADD_FORBID_CONTENTS, Le)), ne && (ae["#text"] = !0), _ && X(ae, ["html", "head", "body"]), ae.table && (X(ae, ["tbody"]), delete ht.tbody), p.TRUSTED_TYPES_POLICY) { if (typeof p.TRUSTED_TYPES_POLICY.createHTML != "function") throw Ds('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'); if (typeof p.TRUSTED_TYPES_POLICY.createScriptURL != "function") throw Ds('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'); z = p.TRUSTED_TYPES_POLICY, O = z.createHTML("") } else z === void 0 && (z = Xf(E, r)), z !== null && typeof O == "string" && (O = z.createHTML("")); Ze && Ze(p), Cn = p }, El = X({}, [...Fr, ...Br, ...Bf]), Al = X({}, [...Hr, ...Hf]), yd = function (p) { let T = $(p); (!T || !T.tagName) && (T = { namespaceURI: Ht, tagName: "template" }); const N = Xn(p.tagName), ce = Xn(T.tagName); return jr[p.namespaceURI] ? p.namespaceURI === Qe ? T.namespaceURI === at ? N === "svg" : T.namespaceURI === De ? N === "svg" && (ce === "annotation-xml" || Vr[ce]) : !!El[N] : p.namespaceURI === De ? T.namespaceURI === at ? N === "math" : T.namespaceURI === Qe ? N === "math" && qr[ce] : !!Al[N] : p.namespaceURI === at ? T.namespaceURI === Qe && !qr[ce] || T.namespaceURI === De && !Vr[ce] ? !1 : !Al[N] && (gd[N] || !El[N]) : !!(es === "application/xhtml+xml" && jr[p.namespaceURI]) : !1 }, vt = function (p) { Sn(t.removed, { element: p }); try { $(p).removeChild(p) } catch { m(p) } }, dn = function (p, T) { try { Sn(t.removed, { attribute: T.getAttributeNode(p), from: T }) } catch { Sn(t.removed, { attribute: null, from: T }) } if (T.removeAttribute(p), p === "is") if (A || S) try { vt(T) } catch { } else try { T.setAttribute(p, "") } catch { } }, Rl = function (p) { let T = null, N = null; if (R) p = "" + p; else { const Me = al(p, /^[\r\n\t ]+/); N = Me && Me[0] } es === "application/xhtml+xml" && Ht === at && (p = '' + p + ""); const ce = z ? z.createHTML(p) : p; if (Ht === at) try { T = new y().parseFromString(ce, es) } catch { } if (!T || !T.documentElement) { T = ee.createDocument(Ht, "template", null); try { T.documentElement.innerHTML = Ur ? O : ce } catch { } } const Ne = T.body || T.documentElement; return p && N && Ne.insertBefore(n.createTextNode(N), Ne.childNodes[0] || null), Ht === at ? fe.call(T, _ ? "html" : "body")[0] : _ ? T.documentElement : Ne }, Cl = function (p) { return V.call(p.ownerDocument || p, p, f.SHOW_ELEMENT | f.SHOW_COMMENT | f.SHOW_TEXT | f.SHOW_PROCESSING_INSTRUCTION | f.SHOW_CDATA_SECTION, null) }, Gr = function (p) { return p instanceof g && (typeof p.nodeName != "string" || typeof p.textContent != "string" || typeof p.removeChild != "function" || !(p.attributes instanceof d) || typeof p.removeAttribute != "function" || typeof p.setAttribute != "function" || typeof p.namespaceURI != "string" || typeof p.insertBefore != "function" || typeof p.hasChildNodes != "function") }, Kr = function (p) { return typeof a == "function" && p instanceof a }; function zt(q, p, T) { kn(q, N => { N.call(t, p, T, Cn) }) } const Pl = function (p) { let T = null; if (zt(Z.beforeSanitizeElements, p, null), Gr(p)) return vt(p), !0; const N = Le(p.nodeName); if (zt(Z.uponSanitizeElement, p, { tagName: N, allowedTags: ae }), v && p.hasChildNodes() && !Kr(p.firstElementChild) && ze(/<[/\w!]/g, p.innerHTML) && ze(/<[/\w!]/g, p.textContent) || v && p.namespaceURI === at && N === "style" && Kr(p.firstElementChild) || p.nodeType === Rn.progressingInstruction || v && p.nodeType === Rn.comment && ze(/<[/\w]/g, p.data)) return vt(p), !0; if (ht[N] || !(Ce.tagCheck instanceof Function && Ce.tagCheck(N)) && !ae[N]) { if (!ht[N] && Ml(N) && (ye.tagNameCheck instanceof RegExp && ze(ye.tagNameCheck, N) || ye.tagNameCheck instanceof Function && ye.tagNameCheck(N))) return !1; if (ne && !ke[N]) { const ce = $(p) || p.parentNode, Ne = Y(p) || p.childNodes; if (Ne && ce) { const Me = Ne.length; for (let st = Me - 1; st >= 0; --st) { const gt = M(Ne[st], !0); ce.insertBefore(gt, H(p)) } } } return vt(p), !0 } return p instanceof l && !yd(p) || (N === "noscript" || N === "noembed" || N === "noframes") && ze(/<\/no(script|embed|frames)/i, p.innerHTML) ? (vt(p), !0) : (k && p.nodeType === Rn.text && (T = p.textContent, kn([Se, Ue, te], ce => { T = En(T, ce, " ") }), p.textContent !== T && (Sn(t.removed, { element: p.cloneNode() }), p.textContent = T)), zt(Z.afterSanitizeElements, p, null), !1) }, Il = function (p, T, N) { if (G[T] || L && (T === "id" || T === "name") && (N in n || N in xd)) return !1; const ce = ge[T] || Ce.attributeCheck instanceof Function && Ce.attributeCheck(T, p); if (!(c && !G[T] && ze(C, T))) { if (!(Ft && ze(pe, T))) { if (!ce || G[T]) { if (!(Ml(p) && (ye.tagNameCheck instanceof RegExp && ze(ye.tagNameCheck, p) || ye.tagNameCheck instanceof Function && ye.tagNameCheck(p)) && (ye.attributeNameCheck instanceof RegExp && ze(ye.attributeNameCheck, T) || ye.attributeNameCheck instanceof Function && ye.attributeNameCheck(T, p)) || T === "is" && ye.allowCustomizedBuiltInElements && (ye.tagNameCheck instanceof RegExp && ze(ye.tagNameCheck, N) || ye.tagNameCheck instanceof Function && ye.tagNameCheck(N)))) return !1 } else if (!Bt[T]) { if (!ze(de, En(N, re, ""))) { if (!((T === "src" || T === "xlink:href" || T === "href") && p !== "script" && ul(N, "data:") === 0 && ot[p])) { if (!(h && !ze(Ge, En(N, re, "")))) { if (N) return !1 } } } } } } return !0 }, wd = X({}, ["annotation-xml", "color-profile", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "missing-glyph"]), Ml = function (p) { return !wd[Xn(p)] && ze(B, p) }, Ol = function (p) { zt(Z.beforeSanitizeAttributes, p, null); const T = p.attributes; if (!T || Gr(p)) return; const N = { attrName: "", attrValue: "", keepAttr: !0, allowedAttributes: ge, forceKeepAttr: void 0 }; let ce = T.length; for (; ce--;) { const Ne = T[ce], Me = Ne.name, st = Ne.namespaceURI, gt = Ne.value, _t = Le(Me), Zr = gt; let Fe = Me === "value" ? Zr : Mf(Zr); if (N.attrName = _t, N.attrValue = Fe, N.keepAttr = !0, N.forceKeepAttr = void 0, zt(Z.uponSanitizeAttribute, p, N), Fe = N.attrValue, j && (_t === "id" || _t === "name") && ul(Fe, K) !== 0 && (dn(Me, p), Fe = K + Fe), v && ze(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, Fe)) { dn(Me, p); continue } if (_t === "attributename" && al(Fe, "href")) { dn(Me, p); continue } if (N.forceKeepAttr) continue; if (!N.keepAttr) { dn(Me, p); continue } if (!b && ze(/\/>/i, Fe)) { dn(Me, p); continue } k && kn([Se, Ue, te], $l => { Fe = En(Fe, $l, " ") }); const Ll = Le(p.nodeName); if (!Il(Ll, _t, Fe)) { dn(Me, p); continue } if (z && typeof E == "object" && typeof E.getAttributeType == "function" && !st) switch (E.getAttributeType(Ll, _t)) { case "TrustedHTML": { Fe = z.createHTML(Fe); break } case "TrustedScriptURL": { Fe = z.createScriptURL(Fe); break } }if (Fe !== Zr) try { st ? p.setAttributeNS(st, Me, Fe) : p.setAttribute(Me, Fe), Gr(p) ? vt(p) : ll(t.removed) } catch { dn(Me, p) } } zt(Z.afterSanitizeAttributes, p, null) }, Yr = function (p) { let T = null; const N = Cl(p); for (zt(Z.beforeSanitizeShadowDOM, p, null); T = N.nextNode();)zt(Z.uponSanitizeShadowNode, T, null), Pl(T), Ol(T), T.content instanceof i && Yr(T.content); zt(Z.afterSanitizeShadowDOM, p, null) }, Bs = function (p) { if (p.nodeType === Rn.element && p.shadowRoot instanceof i) { const ce = p.shadowRoot; Bs(ce), Yr(ce) } const T = p.childNodes; if (!T) return; const N = []; kn(T, ce => { Sn(N, ce) }); for (const ce of N) Bs(ce) }; return t.sanitize = function (q) { + let p = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, T = null, N = null, ce = null, Ne = null; if (Ur = !q, Ur && (q = ""), typeof q != "string" && !Kr(q) && (q = Nf(q), typeof q != "string")) throw Ds("dirty is not a string, aborting"); if (!t.isSupported) return q; if (I || Wr(p), t.removed = [], typeof q == "string" && (he = !1), he) { const gt = q.nodeName; if (typeof gt == "string") { const _t = Le(gt); if (!ae[_t] || ht[_t]) throw Ds("root node is forbidden and cannot be sanitized in-place") } Bs(q) } else if (q instanceof a) T = Rl(""), N = T.ownerDocument.importNode(q, !0), N.nodeType === Rn.element && N.nodeName === "BODY" || N.nodeName === "HTML" ? T = N : T.appendChild(N), Bs(N); else { if (!A && !k && !_ && q.indexOf("<") === -1) return z && U ? z.createHTML(q) : q; if (T = Rl(q), !T) return A ? null : U ? O : "" } T && R && vt(T.firstChild); const Me = Cl(he ? q : T); for (; ce = Me.nextNode();)Pl(ce), Ol(ce), ce.content instanceof i && Yr(ce.content); if (he) return q; if (A) { if (k) { T.normalize(); let gt = T.innerHTML; kn([Se, Ue, te], _t => { gt = En(gt, _t, " ") }), T.innerHTML = gt } if (S) for (Ne = se.call(T.ownerDocument); T.firstChild;)Ne.appendChild(T.firstChild); else Ne = T; return (ge.shadowroot || ge.shadowrootmode) && (Ne = D.call(s, Ne, !0)), Ne } let st = _ ? T.outerHTML : T.innerHTML; return _ && ae["!doctype"] && T.ownerDocument && T.ownerDocument.doctype && T.ownerDocument.doctype.name && ze(Kf, T.ownerDocument.doctype.name) && (st = " +`+ st), k && kn([Se, Ue, te], gt => { st = En(st, gt, " ") }), z && U ? z.createHTML(st) : st + }, t.setConfig = function () { let q = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; Wr(q), I = !0 }, t.clearConfig = function () { Cn = null, I = !1 }, t.isValidAttribute = function (q, p, T) { Cn || Wr({}); const N = Le(q), ce = Le(p); return Il(N, ce, T) }, t.addHook = function (q, p) { typeof p == "function" && Sn(Z[q], p) }, t.removeHook = function (q, p) { if (p !== void 0) { const T = Pf(Z[q], p); return T === -1 ? void 0 : If(Z[q], T, 1)[0] } return ll(Z[q]) }, t.removeHooks = function (q) { Z[q] = [] }, t.removeAllHooks = function () { Z = bl() }, t + } var Qf = xl(); const Jf = { key: 1, class: "flex min-w-0 max-w-[calc(100%-2.5rem)] flex-1 flex-col max-[600px]:max-w-[calc(100%-2.25rem)]" }, ep = { key: 0, class: "flex w-fit flex-col items-start gap-1" }, tp = ["aria-label"], np = { class: "inline-flex items-center gap-1.5" }, sp = { class: "text-[8px] font-semibold tracking-[0.12em] uppercase text-[#3a67c9]" }, rp = { key: 1, class: "flex w-fit max-w-full flex-col items-start gap-2" }, ip = { class: "chat-card relative w-fit max-w-full whitespace-pre-line rounded-[10px_10px_10px_3px] px-4 py-3 text-xs leading-relaxed wrap-anywhere text-slate-900" }, op = ["innerHTML"], lp = { key: 0, class: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-[10px] bg-linear-to-t from-white via-white/92 to-white/0", "aria-hidden": "true" }, ap = { key: 0, class: "flex flex-wrap items-center gap-2" }, up = ["title", "aria-label"], cp = { key: 1, class: "flex flex-wrap items-center" }, fp = ["title", "aria-label"], pp = { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, dp = ["innerHTML"], yl = { __name: "ChatMessage", props: { message: { type: Object, required: !0 }, autoReadEnabled: { type: Boolean, default: !1 }, ttsConfig: { type: Object, default: () => ({ enableVoiceChat: !1, pollyAvailable: !1, usePolly: !0, voiceId: "Zayd" }) } }, setup(e) { const t = e, n = J(!1), s = J(null), r = J(!1), i = J(!1), o = Ee(() => typeof window != "undefined" && "speechSynthesis" in window && "SpeechSynthesisUtterance" in window); function a(O) { typeof window != "undefined" && window.dispatchEvent(new CustomEvent("changai-tts-provider", { detail: { provider: O } })) } function l(O) { if (typeof O != "string") return ""; const P = O.replace(/[\u{1F000}-\u{1FFFF}]/gu, "").replace(/[\u{2600}-\u{26FF}]/gu, "").replace(/[\u{2700}-\u{27BF}]/gu, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/#{1,6}\s+/g, "").replace(/[-*+]\s+/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\s+/g, " "); return P.includes("<") ? (new DOMParser().parseFromString(O, "text/html").body.textContent || "").replace(/\s+/g, " ").trim() : P.trim() } function f() { o.value && window.speechSynthesis.cancel(), s.value && (s.value.pause(), s.value.src = "", s.value = null), n.value = !1 } function u() { var O, P, ee; if (i.value = !i.value, i.value) f(); else { const V = M.value; if (!t.autoReadEnabled || !((O = t.ttsConfig) != null && O.enableVoiceChat) || !V || E()) return; if ((P = t.ttsConfig) != null && P.pollyAvailable && ((ee = t.ttsConfig) != null && ee.usePolly)) { g(V).catch(se => { d(V) }); return } d(V) } } function d(O) { if (!o.value || !O) return; window.dispatchEvent(new CustomEvent("changai-tts-stop")), window.speechSynthesis.cancel(); const P = new SpeechSynthesisUtterance(O); P.rate = 1, P.pitch = 1, P.onend = () => { n.value = !1 }, P.onerror = () => { n.value = !1 }, n.value = !0, a("browser"), window.speechSynthesis.speak(P) } async function g(O) { var fe; const P = await Oc(O, ((fe = t.ttsConfig) == null ? void 0 : fe.voiceId) || "Zayd"); if (!(P != null && P.ok) || !(P != null && P.audio_base64)) throw new Error((P == null ? void 0 : P.error) || "Polly synthesis failed"); window.dispatchEvent(new CustomEvent("changai-tts-stop")), f(); const ee = (P == null ? void 0 : P.mime_type) || "audio/mpeg", V = new Audio(`data:${ee};base64,${P.audio_base64}`); s.value = V, n.value = !0; let se = !1; V.onplay = () => { se = !0, a("polly") }, V.onended = () => { s.value === V && (s.value = null), n.value = !1 }, V.onerror = () => { s.value === V && (s.value = null), n.value = !1 }, await V.play(), se || a("polly") } function y() { f() } function E() { var O; return !!((O = t.message) != null && O.isStatus) } function x(O) { const P = O.target.closest("a"); !P || !P.href || (O.preventDefault(), O.stopPropagation(), window.open(P.href, "_blank", "noopener,noreferrer")) } const M = Ee(() => { var O; return l(((O = t.message) == null ? void 0 : O.text) || "") }), m = Ee(() => { var O; return ((O = t.message) == null ? void 0 : O.role) !== "user" && E() }), H = Ee(() => { var O; return (O = t.message) != null && O.isStatus ? t.message.statusType === "support" ? "Sending to support" : M.value || "Thinking" : "" }), Y = Ee(() => { var ee; if (((ee = t.message) == null ? void 0 : ee.role) === "user" || m.value) return !1; const O = M.value, P = O.split(/\n+/).filter(Boolean).length; return O.length > 520 || P > 8 }), $ = Ee(() => { var O, P; return ((O = t.message) == null ? void 0 : O.role) !== "user" && !m.value && ((P = t.ttsConfig) == null ? void 0 : P.enableVoiceChat) }), z = Ee(() => { var P; const O = ((P = t.message) == null ? void 0 : P.text) || ""; return Qf.sanitize(xe.parse(O)) }); return on(() => t.message.text, async (O, P) => { var se, fe, D; if (!t.autoReadEnabled || t.message.role === "user" || i.value) return; if (!((se = t.ttsConfig) != null && se.enableVoiceChat)) { a("off"); return } const ee = l(O); if (!ee || E()) return; const V = l(P || ""); if (ee !== V) { if ((fe = t.ttsConfig) != null && fe.pollyAvailable && ((D = t.ttsConfig) != null && D.usePolly)) try { await g(ee); return } catch { } d(ee) } }), on(() => t.message.text, () => { r.value = !1, i.value = !1 }), zn(() => { typeof window != "undefined" && window.addEventListener("changai-tts-stop", y) }), ys(() => { typeof window != "undefined" && window.removeEventListener("changai-tts-stop", y), n.value && f() }), (O, P) => (F(), W("div", { class: we(["motion-safe:animate-fade-rise flex w-full gap-1.5", e.message.role === "user" ? "flex-col items-end" : "items-start"]) }, [e.message.role !== "user" ? (F(), Et(Fo, { key: 0 })) : qe("", !0), e.message.role !== "user" ? (F(), W("div", Jf, [m.value ? (F(), W("div", ep, [w("div", { class: "chat-card inline-flex w-fit rounded-[10px_10px_10px_3px] px-3 py-2", role: "status", "aria-live": "polite", "aria-label": H.value }, [w("div", np, [P[1] || (P[1] = w("span", { class: "relative inline-flex h-4 w-4 shrink-0 items-center justify-center" }, [w("span", { class: "absolute inset-0 rounded-full border border-transparent border-t-[#4b89ff] border-r-[#4b89ff]/70 animate-gemini-arc" }), w("svg", { viewBox: "0 0 24 24", class: "relative h-3 w-3 text-[#4b89ff] animate-gemini-spark", "aria-hidden": "true" }, [w("path", { fill: "currentColor", d: "M12 2.8c.52 3.22 1.6 5.66 3.22 7.28 1.62 1.62 4.06 2.7 7.28 3.22-3.22.52-5.66 1.6-7.28 3.22-1.62 1.62-2.7 4.06-3.22 7.28-.52-3.22-1.6-5.66-3.22-7.28-1.62-1.62-4.06-2.7-7.28-3.22 3.22-.52 5.66-1.6 7.28-3.22 1.62-1.62 2.7-4.06 3.22-7.28Z" })])], -1)), w("span", sp, Je(H.value), 1)])], 8, tp)])) : (F(), W("div", rp, [w("div", ip, [w("div", { class: we(["overflow-x-auto", Y.value && !r.value ? "max-h-48 overflow-y-hidden" : ""]), innerHTML: z.value, onClick: x }, null, 10, op), Y.value && !r.value ? (F(), W("div", lp)) : qe("", !0)]), Y.value ? (F(), W("div", ap, [w("button", { type: "button", class: "inline-flex items-center rounded-full border border-slate-200 bg-white px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.08em] text-slate-600 transition-colors duration-200 hover:border-brand-200 hover:text-brand-600", title: r.value ? "Collapse response" : "Expand response", "aria-label": r.value ? "Collapse response" : "Expand response", onClick: P[0] || (P[0] = ee => r.value = !r.value) }, Je(r.value ? "Collapse" : "Expand"), 9, up)])) : qe("", !0), $.value ? (F(), W("div", cp, [w("button", { type: "button", class: we(["inline-flex h-8 w-8 items-center justify-center rounded-full border transition-colors duration-200", i.value ? "border-red-200 bg-red-50 text-red-600 hover:border-red-300 hover:bg-red-100" : "border-green-200 bg-green-50 text-green-600 hover:border-green-300 hover:bg-green-100"]), title: i.value ? "Unmute voice playback" : "Mute voice playback", "aria-label": i.value ? "Unmute voice playback" : "Mute voice playback", onClick: u }, [(F(), W("svg", pp, [P[6] || (P[6] = w("path", { d: "M11 5L6 9H3v6h3l5 4V5Z" }, null, -1)), i.value ? (F(), W(He, { key: 0 }, [P[2] || (P[2] = w("path", { d: "M15 9l4 6" }, null, -1)), P[3] || (P[3] = w("path", { d: "M19 9l-4 6" }, null, -1))], 64)) : (F(), W(He, { key: 1 }, [P[4] || (P[4] = w("path", { d: "M15 10a3 3 0 0 1 0 4" }, null, -1)), P[5] || (P[5] = w("path", { d: "M17.5 7.5a6 6 0 0 1 0 9" }, null, -1))], 64))]))], 10, fp)])) : qe("", !0)]))])) : (F(), W("div", { key: 2, class: "w-fit max-w-[85%] whitespace-pre-line rounded-[13px_13px_3px_13px] bg-linear-to-br from-brand-500 to-brand-600 px-4 py-3 text-[11px] leading-relaxed wrap-anywhere text-white shadow-[0_14px_30px_-18px_rgba(109,79,194,0.85)] max-[600px]:max-w-[88%]", innerHTML: z.value }, null, 8, dp))], 2)) } }, hp = { class: "flex flex-col gap-4 sm:gap-5" }, gp = { class: "motion-safe:animate-fade-rise flex w-full items-start gap-1.5" }, mp = { __name: "ChatTab", props: { messages: { type: Array, required: !0 }, autoReadEnabled: { type: Boolean, default: !1 }, ttsConfig: { type: Object, required: !0 } }, setup(e) { return (t, n) => (F(), W("div", hp, [w("div", gp, [Oe(Fo), n[0] || (n[0] = w("p", { class: "w-fit max-w-[calc(100%-2.5rem)] whitespace-pre-line rounded-[10px_10px_10px_3px] bg-brand-50 px-4 py-3 text-xs leading-relaxed wrap-anywhere text-slate-900 max-[600px]:max-w-[calc(100%-2.25rem)]" }, [Ss(" Hello there 👋 I am ChangAI from "), w("a", { target: "_blank", href: "https://erpgulf.com", rel: "noopener noreferrer", style: { color: "#1e90ff" } }, "ERPGulf.com"), Ss(", your ERP assistant."), w("br"), w("a", { target: "_blank", href: "https://app.erpgulf.com/en/articles/chang-ai-quick-start-guide", rel: "noopener noreferrer", style: { color: "#1e90ff" } }, "ChangAI Quick Start Guide - Click here.")], -1))]), (F(!0), W(He, null, ws(e.messages, (s, r) => (F(), Et(yl, { key: r, message: s, autoReadEnabled: e.autoReadEnabled, ttsConfig: e.ttsConfig }, null, 8, ["message", "autoReadEnabled", "ttsConfig"]))), 128))])) } }; function wl(e) { try { return JSON.stringify(e, null, 2) } catch { return String(e) } } function vl(e) { var t, n; return (e == null ? void 0 : e.message) || ((t = e == null ? void 0 : e.responseJSON) == null ? void 0 : t.exception) || ((n = e == null ? void 0 : e.responseJSON) == null ? void 0 : n.message) || (e == null ? void 0 : e.responseText) || String(e) } function bp(e) { return typeof e == "string" ? e : e && typeof e == "object" ? e.error ? `⚠️ ${e.error}` : e.answer || e.text || "" : "" } const xp = { key: 0, class: "rounded-lg bg-brand-50 px-4 py-3 text-xs text-black" }, yp = { class: "whitespace-pre-wrap wrap-anywhere text-[11px] leading-relaxed text-black" }, wp = { key: 1, class: "mb-3 min-w-0 overflow-x-auto rounded-lg bg-brand-50 p-2 text-[11px]" }, vp = { class: "whitespace-pre-wrap wrap-anywhere text-[11px] leading-relaxed text-black" }, _p = { __name: "DebugTab", props: { logs: { type: Array, required: !0 }, currentDebug: { type: Object, default: null } }, setup(e) { const t = new Set(["gemini_json_content", "private_key", "private_key_id", "client_secret", "client_id", "aws_access_key", "aws_secret_key", "api_key", "token", "access_token", "refresh_token", "password", "secret", "authorization", "embed_version_id", "llm_version_id", "entity_retriever", "retriever", "deploy_url", "support_api_url", "get_ticket_details_url"]); function n(r, i = 0) { if (i > 10 || r === null || r === void 0 || typeof r == "string" || typeof r == "number" || typeof r == "boolean") return r; if (Array.isArray(r)) return r.map(o => n(o, i + 1)); if (typeof r == "object") { const o = {}; for (const [a, l] of Object.entries(r)) t.has(a.toLowerCase()) || (o[a] = n(l, i + 1)); return o } return r } function s(r) { return wl(n(r)) } return (r, i) => (F(), W("div", null, [e.logs.length === 0 ? (F(), W("p", xp, "No debug data yet.")) : qe("", !0), (F(!0), W(He, null, ws(e.logs, (o, a) => (F(), W("div", { key: a, class: "mb-3 min-w-0 overflow-x-auto rounded-lg bg-gray-100 p-2 text-[11px]" }, [w("pre", yp, Je(s(o)), 1)]))), 128)), e.currentDebug ? (F(), W("div", wp, [w("pre", vp, Je(s(e.currentDebug)), 1)])) : qe("", !0)])) } }, Tp = { class: "flex flex-col gap-4 sm:gap-5" }, kp = { key: 0, class: "chat-card motion-safe:animate-fade-rise rounded-lg px-4 py-3 text-xs text-slate-900" }, Sp = { __name: "SupportTab", props: { messages: { type: Array, required: !0 }, autoReadEnabled: { type: Boolean, default: !1 }, ttsConfig: { type: Object, required: !0 } }, setup(e) { return (t, n) => (F(), W("div", Tp, [e.messages.length === 0 ? (F(), W("p", kp, "Send a message to Support.")) : qe("", !0), (F(!0), W(He, null, ws(e.messages, (s, r) => (F(), Et(yl, { key: r, message: s, autoReadEnabled: e.autoReadEnabled, ttsConfig: e.ttsConfig }, null, 8, ["message", "autoReadEnabled", "ttsConfig"]))), 128))])) } }, Ep = { class: "flex flex-col gap-4" }, Ap = { class: "chat-card motion-safe:animate-fade-rise rounded-xl p-4" }, Rp = { class: "flex items-start justify-between gap-4" }, Cp = ["aria-pressed", "title"], Pp = { class: "chat-card motion-safe:animate-fade-rise rounded-xl p-4" }, Ip = { class: "flex items-start justify-between gap-4" }, Mp = { class: "mt-2 text-[11px] text-slate-500" }, Op = { key: 0, class: "mt-1 text-[11px] text-slate-500" }, Lp = { key: 1, class: "mt-1 text-[11px] text-slate-500" }, $p = ["aria-pressed", "disabled"], Dp = { key: 0, class: "mt-3 rounded-md bg-amber-50 px-2.5 py-2 text-xs text-amber-700" }, Np = { key: 1, class: "mt-3 rounded-md bg-amber-50 px-2.5 py-2 text-xs text-amber-700" }, Fp = { class: "chat-card motion-safe:animate-fade-rise rounded-xl p-4" }, Bp = { class: "flex items-start justify-between gap-4" }, Hp = ["aria-pressed", "title"], zp = { class: "chat-card motion-safe:animate-fade-rise rounded-xl p-4" }, Up = { class: "flex items-start justify-between gap-4" }, jp = ["aria-pressed", "title"], Vp = { __name: "SettingsTab", props: { autoReadEnabled: { type: Boolean, required: !0 }, ttsConfig: { type: Object, required: !0 }, settings: { type: Object, default: null }, debugEnabled: { type: Boolean, default: !1 }, sendNonERPtoaiEnabled: { type: Boolean, default: !1 } }, emits: ["toggleAutoRead", "togglePollyPreference", "toggleDebug", "toggleSendNonERP"], setup(e) { const t = e, n = Ee(() => { var s, r; return (s = t.ttsConfig) != null && s.enableVoiceChat ? (r = t.ttsConfig) != null && r.pollyAvailable ? "Available" : "Unavailable" : "Voice disabled on server" }); return (s, r) => { var i, o, a, l, f, u, d, g, y, E, x, M, m, H, Y; return F(), W("div", Ep, [r[10] || (r[10] = w("div", { class: "chat-card motion-safe:animate-fade-rise rounded-xl p-4 text-slate-900" }, [w("h3", { class: "text-sm font-semibold tracking-[0.01em]" }, "Speech Settings"), w("p", { class: "mt-1 text-xs leading-relaxed text-slate-600" }, "These controls apply only inside this chatbot box for the current browser session.")], -1)), w("div", Ap, [w("div", Rp, [r[4] || (r[4] = w("div", null, [w("p", { class: "text-sm font-semibold text-slate-900" }, "Auto Read Replies"), w("p", { class: "mt-1 text-xs text-slate-600" }, "Automatically read bot replies aloud.")], -1)), w("button", { class: we(["group relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200", e.autoReadEnabled ? "bg-emerald-500/95" : "bg-slate-300"]), "aria-pressed": e.autoReadEnabled ? "true" : "false", title: e.autoReadEnabled ? "Disable auto read" : "Enable auto read", onClick: r[0] || (r[0] = $ => s.$emit("toggleAutoRead")) }, [w("span", { class: we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200", e.autoReadEnabled ? "left-[1.45rem]" : "left-0.5"]) }, null, 2)], 10, Cp)]), w("p", { class: we(["mt-3 text-[11px] font-medium", e.autoReadEnabled ? "text-emerald-700" : "text-slate-500"]) }, Je(e.autoReadEnabled ? "Auto read is active." : "Auto read is currently off."), 3)]), w("div", Pp, [w("div", Ip, [w("div", null, [r[5] || (r[5] = w("p", { class: "text-sm font-semibold text-slate-900" }, "Use Amazon Polly", -1)), r[6] || (r[6] = w("p", { class: "mt-1 text-xs text-slate-600" }, "Use Polly when available; otherwise browser speech is used automatically.", -1)), w("p", Mp, "Availability: " + Je(n.value), 1), (i = e.settings) != null && i.aws_region ? (F(), W("p", Op, "Region: " + Je(e.settings.aws_region), 1)) : qe("", !0), (o = e.ttsConfig) != null && o.voiceId ? (F(), W("p", Lp, "Voice: " + Je(e.ttsConfig.voiceId), 1)) : qe("", !0)]), w("button", { class: we(["relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200 disabled:cursor-not-allowed disabled:opacity-55", (a = e.ttsConfig) != null && a.usePolly && ((l = e.ttsConfig) != null && l.enableVoiceChat) && ((f = e.ttsConfig) != null && f.pollyAvailable) ? "bg-emerald-500/95" : "bg-slate-300"]), "aria-pressed": (u = e.ttsConfig) != null && u.usePolly && ((d = e.ttsConfig) != null && d.enableVoiceChat) && ((g = e.ttsConfig) != null && g.pollyAvailable) ? "true" : "false", disabled: !((y = e.ttsConfig) != null && y.pollyAvailable) || !((E = e.ttsConfig) != null && E.enableVoiceChat), onClick: r[1] || (r[1] = $ => s.$emit("togglePollyPreference")) }, [w("span", { class: we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200", (x = e.ttsConfig) != null && x.usePolly && ((M = e.ttsConfig) != null && M.enableVoiceChat) && ((m = e.ttsConfig) != null && m.pollyAvailable) ? "left-[1.45rem]" : "left-0.5"]) }, null, 2)], 10, $p)]), (H = e.ttsConfig) != null && H.enableVoiceChat ? (Y = e.ttsConfig) != null && Y.pollyAvailable ? qe("", !0) : (F(), W("p", Np, "Polly is not available for this site. Browser speech will be used.")) : (F(), W("p", Dp, "Voice chat is disabled in ChangAI Settings."))]), w("div", Fp, [w("div", Bp, [w("div", null, [r[7] || (r[7] = w("p", { class: "text-sm font-semibold text-slate-900" }, "Enable Debug Tab", -1)), r[8] || (r[8] = w("p", { class: "mt-1 text-xs text-slate-600" }, " Show or hide the Debug tab inside this chatbot. ", -1)), w("p", { class: we(["mt-2 text-[11px] font-medium", e.debugEnabled ? "text-emerald-700" : "text-slate-500"]) }, Je(e.debugEnabled ? "Debug tab is active." : "Debug tab is currently off."), 3)]), w("button", { type: "button", class: we(["relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200", e.debugEnabled ? "bg-emerald-500/95" : "bg-slate-300"]), "aria-pressed": e.debugEnabled ? "true" : "false", title: e.debugEnabled ? "Disable debug tab" : "Enable debug tab", onClick: r[2] || (r[2] = $ => s.$emit("toggleDebug")) }, [w("span", { class: we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200", e.debugEnabled ? "left-[1.45rem]" : "left-0.5"]) }, null, 2)], 10, Hp)])]), w("div", zp, [w("div", Up, [r[9] || (r[9] = w("div", null, [w("p", { class: "text-sm font-semibold text-slate-900" }, "Send non-ERP questions directly to AI"), w("p", { class: "mt-1 text-xs text-slate-600" }, "Questions unrelated to your ERP will skip the system and go straight to AI")], -1)), w("button", { class: we(["group relative h-7 w-12 shrink-0 rounded-full border border-slate-200 transition-all duration-200", e.sendNonERPtoaiEnabled ? "bg-emerald-500/95" : "bg-slate-300"]), "aria-pressed": e.sendNonERPtoaiEnabled ? "true" : "false", title: e.sendNonERPtoaiEnabled ? "Non-ERP questions are being sent directly to AI" : "Enable direct AI reply for non-ERP questions", onClick: r[3] || (r[3] = $ => s.$emit("toggleSendNonERP")) }, [w("span", { class: we(["absolute top-0.5 h-5.5 w-5.5 rounded-full bg-white shadow-sm transition-all duration-200", e.sendNonERPtoaiEnabled ? "left-[1.45rem]" : "left-0.5"]) }, null, 2)], 10, jp)]), w("p", { class: we(["mt-3 text-[11px] font-medium", e.sendNonERPtoaiEnabled ? "text-emerald-700" : "text-slate-500"]) }, Je(e.sendNonERPtoaiEnabled ? "Non-ERP questions are now routed directly to AI" : "Direct AI routing is currently off"), 3)])]) } } }, qp = { key: 0, class: "pointer-events-none absolute -top-14 left-0 right-0 z-20 flex justify-center px-2", role: "status", "aria-live": "polite" }, Wp = { __name: "StatusToast", props: { visible: { type: Boolean, required: !0 }, message: { type: String, default: "" }, type: { type: String, default: "info" }, dismissible: { type: Boolean, default: !0 } }, emits: ["close"], setup(e) { const t = e, n = Ee(() => t.type === "error" ? "bg-red-50 text-red-700 ring-red-200" : (t.type === "listening", "bg-blue-50 text-blue-700 ring-blue-200")), s = Ee(() => t.type === "error" ? "bg-red-500" : t.type === "listening" ? "bg-blue-500 animate-pulse" : "bg-blue-500"); return (r, i) => (F(), Et(Pu, { "enter-active-class": "transition duration-200 ease-out", "enter-from-class": "translate-y-1 opacity-0", "enter-to-class": "translate-y-0 opacity-100", "leave-active-class": "transition duration-150 ease-in", "leave-from-class": "translate-y-0 opacity-100", "leave-to-class": "translate-y-1 opacity-0" }, { default: Di(() => [e.visible ? (F(), W("div", qp, [w("div", { class: we(["pointer-events-auto flex max-w-[92%] items-start gap-2 rounded-lg px-3 py-2 text-xs shadow-lg ring-1", n.value]) }, [w("span", { class: we(["mt-0.5 h-2 w-2 shrink-0 rounded-full", s.value]) }, null, 2), w("span", null, Je(e.message), 1), e.dismissible ? (F(), W("button", { key: 0, type: "button", class: "ml-1 appearance-none border-0 text-current/80 transition hover:text-current focus:outline-none", "aria-label": "Dismiss notification", onClick: i[0] || (i[0] = o => r.$emit("close")) }, " × ")) : qe("", !0)], 2)])) : qe("", !0)]), _: 1 })) } }, Gp = { class: "relative w-full" }, Kp = ["placeholder", "disabled"], Yp = ["title", "aria-label", "disabled"], Zp = { key: 0, viewBox: "0 0 24 24", width: "16", height: "16", fill: "currentColor", "aria-hidden": "true" }, Xp = { key: 1, viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, Qp = { key: 2, viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true", class: "animate-spin" }, Jp = ["title", "aria-label", "disabled"], ed = { key: 0, viewBox: "0 0 24 24", width: "18", height: "18", fill: "none", "aria-hidden": "true", class: "text-rose-600 motion-safe:animate-stop-button-pulse" }, td = { key: 1, viewBox: "0 0 24 24", width: "16", height: "16", fill: "currentColor", "aria-hidden": "true" }, nd = { __name: "ChatForm", props: { placeholder: { type: String, default: "Message..." }, disabled: { type: Boolean, default: !1 }, isAwaitingResponse: { type: Boolean, default: !1 } }, emits: ["submit", "cancel"], setup(e, { expose: t, emit: n }) { const s = e, r = n, i = J(""), o = J(null), a = J(!1), l = J(!1), f = J(!1), u = J(!1), d = J(!1), g = J(!1), y = J(!1), E = J(""), x = J("info"), M = J("Voice input is unavailable in this browser/context."); let m = null, H = null, Y = null; const $ = J(""), z = J(!1), O = Ee(() => f.value ? "Requesting microphone permission..." : u.value ? "Starting voice input..." : d.value ? "Stopping voice input..." : l.value ? a.value ? "Stop voice input" : "Start voice input" : "Voice input is unavailable in this browser/context"), P = Ee(() => s.isAwaitingResponse ? "Stop response" : "Send"), ee = Ee(() => s.isAwaitingResponse ? !1 : s.disabled || !i.value.trim()), V = Ee(() => s.isAwaitingResponse ? "bg-white border border-rose-100 shadow-[0_8px_20px_-12px_rgba(159,18,57,0.35)] hover:bg-rose-50" : "bg-linear-to-br from-brand-500 to-brand-600 text-white shadow-[0_10px_24px_-16px_rgba(109,79,194,0.85)] hover:from-brand-600 hover:to-violet-700"); function se() { return typeof window == "undefined" ? null : window.SpeechRecognition || window.webkitSpeechRecognition || null } function fe() { var ae; const re = se(), B = typeof window != "undefined" ? window.isSecureContext : !1, de = typeof navigator != "undefined" && !!((ae = navigator.mediaDevices) != null && ae.getUserMedia); l.value = !!(re && B && de), B ? (!de || !re) && (M.value = "Voice input is not supported in this browser.") : M.value = "Voice input requires HTTPS (or localhost).", re && l.value && (m = new re, m.continuous = !0, m.interimResults = !0, m.lang = typeof navigator != "undefined" && navigator.language || "en-US", m.onstart = () => { a.value = !0, u.value = !1, d.value = !1, D(), C("Listening... Tap mic to stop", "listening", { persistent: !0, key: "listening" }) }, m.onend = () => { a.value = !1, u.value = !1, d.value = !1, D(), $.value === "listening" && pe(), z.value && (z.value = !1, Ge()) }, m.onerror = ve => { if (a.value = !1, u.value = !1, d.value = !1, D(), z.value = !1, (ve == null ? void 0 : ve.error) === "not-allowed" || (ve == null ? void 0 : ve.error) === "service-not-allowed") { C("Microphone permission denied. Please allow microphone access in browser settings.", "error"); return } if ((ve == null ? void 0 : ve.error) === "audio-capture") { C("No microphone detected. Please connect a microphone and try again.", "error"); return } if ((ve == null ? void 0 : ve.error) === "no-speech") { C("No speech detected. Try speaking a bit louder.", "info"); return } C("Voice input failed. Please try again.", "error") }, m.onresult = ve => { let ge = ""; for (let nt = ve.resultIndex; nt < ve.results.length; nt += 1)ge += ve.results[nt][0].transcript; i.value = ge.trimStart() }) } function D() { Y && (clearTimeout(Y), Y = null) } function Z(re = {}) { const { submitAfterStop: B = !1 } = re; if (m) { z.value = B, u.value = !1, d.value = !0, D(); try { m.stop() } catch { d.value = !1, B && (z.value = !1, Ge()); return } Y = setTimeout(() => { if (m && !(!a.value && !d.value)) try { m.abort() } catch { d.value = !1, z.value = !1 } }, 1200) } } function Se() { if (!l.value || !m) { C(M.value, "error"); return } if (a.value || u.value || d.value) { Z({ submitAfterStop: a.value }); return } te() } async function Ue() { var re; if (g.value) return !0; if (!((re = navigator.mediaDevices) != null && re.getUserMedia)) return C("Microphone API is unavailable in this browser.", "error"), !1; f.value = !0, C("Requesting microphone permission...", "info", { persistent: !0, key: "requesting" }); try { return (await navigator.mediaDevices.getUserMedia({ audio: !0 })).getTracks().forEach(de => de.stop()), g.value = !0, !0 } catch (B) { return (B == null ? void 0 : B.name) === "NotAllowedError" || (B == null ? void 0 : B.name) === "SecurityError" ? C("Microphone permission denied. Please allow it and try again.", "error") : (B == null ? void 0 : B.name) === "NotFoundError" ? C("No microphone found on this device.", "error") : C("Unable to access microphone. Please check browser permissions.", "error"), !1 } finally { f.value = !1, $.value === "requesting" && pe() } } async function te() { var B; if (!(!m || a.value || u.value || d.value || !await Ue() || !m)) { z.value = !1, (B = o.value) == null || B.focus(), u.value = !0; try { m.start() } catch (de) { u.value = !1, d.value = !1, (de == null ? void 0 : de.name) !== "InvalidStateError" && C("Unable to start voice input. Please try again.", "error") } } } function C(re, B = "info", de = {}) { const { duration: ae = 4200, persistent: ve = !1, key: ge = "" } = de; E.value = re, x.value = B, $.value = ge, y.value = !0, H && clearTimeout(H), ve || (H = setTimeout(() => { y.value = !1, $.value = "" }, ae)) } function pe() { y.value = !1, $.value = "", H && (clearTimeout(H), H = null) } function Ge() { if (s.isAwaitingResponse) { r("cancel"); return } const re = i.value.trim(); re && ((a.value || u.value || d.value) && m && Z({ submitAfterStop: !1 }), r("submit", re), i.value = "") } return t({ focus: () => { var re; return (re = o.value) == null ? void 0 : re.focus() } }), zn(() => { fe() }), ys(() => { if (m && (a.value || u.value || d.value)) { z.value = !1, D(); try { m.abort() } catch { } } D(), pe() }), (re, B) => (F(), W("div", Gp, [w("form", { class: "group flex min-h-11 items-center gap-2 rounded-full border border-slate-200/90 bg-white/95 px-3 shadow-[0_12px_26px_-20px_rgba(15,23,42,0.7)] transition-all duration-250 focus-within:-translate-y-0.5 focus-within:border-brand-200 focus-within:shadow-[0_18px_30px_-20px_rgba(13,110,253,0.5)] focus-within:ring-2 focus-within:ring-brand-500/25", style: { "border-radius": "9999px" }, autocomplete: "off", onSubmit: pt(Ge, ["prevent"]), onClick: B[5] || (B[5] = pt(() => { }, ["stop"])), onMousedown: B[6] || (B[6] = pt(() => { }, ["stop"])), onKeydown: B[7] || (B[7] = pt(() => { }, ["stop"])), onKeyup: B[8] || (B[8] = pt(() => { }, ["stop"])) }, [Pa(w("input", { ref_key: "inputRef", ref: o, type: "text", "onUpdate:modelValue": B[0] || (B[0] = de => i.value = de), class: "h-11 w-full border-none bg-transparent text-sm font-medium text-slate-800 placeholder:text-slate-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50", placeholder: e.disabled ? "Waiting for response..." : e.placeholder, disabled: e.disabled, required: "", onKeydown: B[1] || (B[1] = pt(() => { }, ["stop"])), onKeyup: B[2] || (B[2] = pt(() => { }, ["stop"])), onKeypress: B[3] || (B[3] = pt(() => { }, ["stop"])), onInput: B[4] || (B[4] = pt(() => { }, ["stop"])) }, null, 40, Kp), [[Xu, i.value]]), w("button", { type: "button", class: we(["grid h-8 w-8 shrink-0 appearance-none place-items-center rounded-full border border-transparent text-slate-600 transition-all duration-200 hover:-translate-y-0.5 hover:border-slate-200 hover:bg-slate-100 hover:text-slate-900 focus:outline-none disabled:cursor-not-allowed disabled:opacity-40", a.value ? "border-red-200 bg-red-100 text-red-600 shadow-[0_10px_20px_-18px_rgba(220,38,38,0.9)] hover:bg-red-100 hover:text-red-600" : ""]), style: { "border-radius": "9999px" }, title: O.value, "aria-label": O.value, disabled: e.disabled || !l.value || f.value || u.value || d.value, onClick: Se }, [a.value && !f.value ? (F(), W("svg", Zp, B[9] || (B[9] = [w("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }, null, -1)]))) : f.value ? (F(), W("svg", Qp, B[11] || (B[11] = [w("circle", { cx: "12", cy: "12", r: "9", opacity: "0.3" }, null, -1), w("path", { d: "M21 12a9 9 0 0 1-9 9" }, null, -1)]))) : (F(), W("svg", Xp, B[10] || (B[10] = [w("path", { d: "M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z" }, null, -1), w("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }, null, -1), w("path", { d: "M12 19v3" }, null, -1)])))], 10, Yp), w("button", { type: "submit", title: P.value, "aria-label": P.value, class: we(["grid h-8 w-8 shrink-0 appearance-none place-items-center rounded-full border-0 transition-all duration-200 hover:-translate-y-0.5 focus:outline-none disabled:cursor-not-allowed disabled:opacity-40", V.value]), style: { "border-radius": "9999px" }, disabled: ee.value }, [e.isAwaitingResponse ? (F(), W("svg", ed, B[12] || (B[12] = [w("circle", { cx: "12", cy: "12", r: "8", stroke: "currentColor", "stroke-width": "2.1", class: "opacity-95" }, null, -1), w("rect", { x: "9", y: "9", width: "6", height: "6", rx: "1.35", fill: "currentColor" }, null, -1)]))) : (F(), W("svg", td, B[13] || (B[13] = [w("path", { d: "M4 12l1.41 1.41L11 7.83V20h2V7.83l5.59 5.58L20 12l-8-8-8 8z" }, null, -1)])))], 10, Jp)], 32), Oe(Wp, { visible: y.value, message: E.value, type: x.value, dismissible: x.value !== "listening", onClose: pe }, null, 8, ["visible", "message", "type", "dismissible"])])) } }, sd = { class: "relative overflow-hidden bg-linear-to-br from-brand-600 via-brand-500 to-violet-400" }, rd = { class: "min-w-0" }, id = { key: 1, class: "border-t border-slate-200/80 bg-white/90 px-3 py-3 pb-[calc(12px+env(safe-area-inset-bottom))] backdrop-blur-sm sm:px-4 sm:py-4" }, od = 56, ld = { __name: "ChatbotPopup", props: { isOpen: { type: Boolean, required: !0 }, activeTab: { type: String, required: !0 }, debugEnabled: { type: Boolean, default: !1 }, sendNonERPtoaiEnabled: { type: Boolean, default: !1 }, chatHistory: { type: Array, required: !0 }, debugLogs: { type: Array, required: !0 }, currentDebug: { type: Object, default: null }, supportHistory: { type: Array, required: !0 }, autoReadEnabled: { type: Boolean, required: !0 }, ttsConfig: { type: Object, required: !0 }, activeTtsProvider: { type: String, required: !0 }, settings: { type: Object, default: null }, isAwaitingChatResponse: { type: Boolean, default: !1 }, isAwaitingSupportResponse: { type: Boolean, default: !1 } }, emits: ["close", "submit", "cancelResponse", "update:activeTab", "toggleAutoRead", "togglePollyPreference", "toggleDebug", "toggleSendNonERP"], setup(e, { expose: t, emit: n }) { const s = e, r = n, i = J(null), o = J(null), a = J(s.activeTab), l = J("default"), f = J(!1), u = Ee(() => a.value === "support" ? s.isAwaitingSupportResponse : a.value === "chat" ? s.isAwaitingChatResponse : !1); function d() { const M = i.value; if (!s.isOpen || !M) { f.value = !1; return } const m = M.scrollHeight - M.clientHeight; if (m <= 4) { f.value = !1; return } const H = m - M.scrollTop; f.value = H > od } function g() { const M = i.value; M && (M.scrollTo({ top: M.scrollHeight, behavior: "smooth" }), setTimeout(() => { d() }, 220)) } function y() { St(() => { d() }) } function E() { if (l.value === "default") { l.value = "half"; return } if (l.value === "half") { l.value = "full"; return } l.value = "default" } const x = Ee(() => { const M = "chat-shell fixed z-[9999] flex min-h-0 flex-col overflow-hidden border border-slate-200/80 shadow-[0_32px_80px_-44px_rgba(2,6,23,0.7),0_18px_40px_-24px_rgba(15,23,42,0.45)] transition-all duration-300 ease-out origin-bottom-right", m = s.isOpen ? "pointer-events-auto opacity-100 translate-x-0 translate-y-0 scale-100 motion-safe:animate-surface-in" : "pointer-events-none opacity-0 translate-x-1/5 translate-y-8 scale-95"; return l.value === "full" ? [M, m, "inset-0 h-screen w-screen max-h-screen max-w-screen rounded-none origin-center"] : l.value === "half" ? [M, m, "bottom-[74px] right-5 h-[min(86vh,860px)] w-[min(50vw,860px)] rounded-2xl", "max-[900px]:bottom-[78px] max-[900px]:right-3 max-[900px]:h-[min(86vh,760px)] max-[900px]:w-[min(70vw,760px)] max-[900px]:rounded-[14px]", "max-[600px]:inset-0 max-[600px]:h-screen max-[600px]:w-screen max-[600px]:max-h-screen max-[600px]:max-w-screen max-[600px]:rounded-none max-[600px]:pb-[env(safe-area-inset-bottom)]"] : [M, m, "bottom-[74px] right-5 h-[min(560px,72vh)] w-[min(360px,calc(100vw-40px))] rounded-2xl", "max-[900px]:bottom-[78px] max-[900px]:right-3 max-[900px]:h-[min(70vh,540px)] max-[900px]:w-[min(360px,calc(100vw-24px))] max-[900px]:rounded-[14px]", "max-[600px]:inset-0 max-[600px]:h-screen max-[600px]:w-screen max-[600px]:max-h-screen max-[600px]:max-w-screen max-[600px]:rounded-none max-[600px]:pb-[env(safe-area-inset-bottom)]"] }); return on(() => s.activeTab, M => { a.value = M, y() }), on(() => s.isOpen, M => { M && a.value !== "settings" && St(() => { var m; return (m = o.value) == null ? void 0 : m.focus() }), y() }), on(a, M => { r("update:activeTab", M), y() }), on(() => [s.chatHistory.length, s.supportHistory.length, s.debugLogs.length, s.currentDebug], () => { y() }), on(() => s.debugEnabled, M => { !M && a.value === "debug" && (a.value = "chat"), y() }), zn(() => { y() }), t({ scrollToBottom() { St(() => { g() }) } }), (M, m) => (F(), W("div", { class: we(x.value), onKeydown: m[9] || (m[9] = pt(() => { }, ["stop"])), onKeyup: m[10] || (m[10] = pt(() => { }, ["stop"])), onKeypress: m[11] || (m[11] = pt(() => { }, ["stop"])) }, [m[14] || (m[14] = w("div", { class: "pointer-events-none absolute -right-14 -top-14 h-36 w-36 rounded-full bg-brand-500/15 blur-2xl" }, null, -1)), m[15] || (m[15] = w("div", { class: "pointer-events-none absolute -bottom-14 -left-12 h-32 w-32 rounded-full bg-violet-400/15 blur-2xl" }, null, -1)), w("div", sd, [m[12] || (m[12] = w("div", { class: "pointer-events-none absolute inset-0 opacity-45", style: { background: "linear-gradient(120deg, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0.02) 52%, rgba(255,255,255,0.12) 100%)" } }, null, -1)), Oe(vc, { windowMode: l.value, autoReadEnabled: e.autoReadEnabled, activeTtsProvider: e.activeTtsProvider, onClose: m[0] || (m[0] = H => M.$emit("close")), onCycleResize: E, onToggleAutoRead: m[1] || (m[1] = H => M.$emit("toggleAutoRead")) }, null, 8, ["windowMode", "autoReadEnabled", "activeTtsProvider"]), Oe(Sc, { modelValue: a.value, "onUpdate:modelValue": m[2] || (m[2] = H => a.value = H), debugEnabled: e.debugEnabled }, null, 8, ["modelValue", "debugEnabled"])]), w("div", { class: "chat-scrollbar min-h-0 flex-1 overflow-x-hidden overflow-y-scroll bg-slate-50/60 px-4 py-4 max-[900px]:px-3.5 max-[900px]:py-3.5 max-[600px]:px-3 max-[600px]:py-3", ref_key: "chatBodyRef", ref: i, onScrollPassive: d }, [w("div", rd, [a.value === "chat" ? (F(), Et(mp, { key: 0, messages: e.chatHistory, autoReadEnabled: e.autoReadEnabled, ttsConfig: e.ttsConfig }, null, 8, ["messages", "autoReadEnabled", "ttsConfig"])) : a.value === "debug" && e.debugEnabled ? (F(), Et(_p, { key: 1, logs: e.debugLogs, currentDebug: e.currentDebug }, null, 8, ["logs", "currentDebug"])) : a.value === "support" ? (F(), Et(Sp, { key: 2, messages: e.supportHistory, autoReadEnabled: e.autoReadEnabled, ttsConfig: e.ttsConfig }, null, 8, ["messages", "autoReadEnabled", "ttsConfig"])) : a.value === "settings" ? (F(), Et(Vp, { key: 3, autoReadEnabled: e.autoReadEnabled, ttsConfig: e.ttsConfig, settings: e.settings, debugEnabled: e.debugEnabled, sendNonERPtoaiEnabled: e.sendNonERPtoaiEnabled, onToggleAutoRead: m[3] || (m[3] = H => M.$emit("toggleAutoRead")), onTogglePollyPreference: m[4] || (m[4] = H => M.$emit("togglePollyPreference")), onToggleDebug: m[5] || (m[5] = H => M.$emit("toggleDebug")), onToggleSendNonERP: m[6] || (m[6] = H => M.$emit("toggleSendNonERP")) }, null, 8, ["autoReadEnabled", "ttsConfig", "settings", "debugEnabled", "sendNonERPtoaiEnabled"])) : qe("", !0)])], 544), f.value ? (F(), W("button", { key: 0, type: "button", class: we(["absolute right-4 z-20 grid h-9 w-9 place-items-center rounded-full border border-brand-200/70 bg-white/95 text-brand-600 shadow-[0_14px_26px_-16px_rgba(15,23,42,0.65)] transition-all duration-200 hover:-translate-y-0.5 hover:border-brand-300 hover:text-brand-700 focus:outline-none", a.value !== "settings" ? "bottom-[calc(90px+env(safe-area-inset-bottom))] sm:bottom-[96px]" : "bottom-4 sm:bottom-5"]), title: "Scroll to bottom", "aria-label": "Scroll to bottom", onClick: g }, m[13] || (m[13] = [w("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, [w("path", { d: "M7 10l5 5 5-5" })], -1)]), 2)) : qe("", !0), a.value !== "settings" ? (F(), W("div", id, [Oe(nd, { ref_key: "chatFormRef", ref: o, placeholder: a.value === "support" ? "Message Support..." : "Message...", disabled: u.value, isAwaitingResponse: u.value, onSubmit: m[7] || (m[7] = H => M.$emit("submit", H)), onCancel: m[8] || (m[8] = H => M.$emit("cancelResponse")) }, null, 8, ["placeholder", "disabled", "isAwaitingResponse"])])) : qe("", !0)], 34)) } }, _l = "changai_chat_id", Tl = "changai_polly_enabled"; function ad() { let e = sessionStorage.getItem(_l); return e || (e = `session_${Date.now()}_${crypto.randomUUID()}`, sessionStorage.setItem(_l, e)), e } function ud() { const e = localStorage.getItem(Tl); return e === null ? !0 : e === "true" } function cd(e) { localStorage.setItem(Tl, String(!!e)) } const fd = { __name: "App", setup(e) { const t = J(!1), n = J("chat"), s = J([]), r = J([]), i = J(!1), o = J([]), a = J(null), l = J("actual"), f = J(!0), u = J(null), d = J(!1), g = J(null), y = J(!1), E = J({ enableVoiceChat: !1, pollyAvailable: !1, usePolly: !0, voiceId: "Zayd", enable_changai: !1 }), x = J("off"), M = J(null), m = J(null), H = Ee(() => M.value !== null), Y = Ee(() => m.value !== null); function $() { if (!E.value.enableVoiceChat) { x.value = "off"; return } x.value = E.value.usePolly ? "polly" : "browser" } function z(te) { var pe; const C = (pe = te == null ? void 0 : te.detail) == null ? void 0 : pe.provider; (C === "polly" || C === "browser" || C === "off") && (x.value = C) } async function O() { var pe, Ge, re, B, de; if (!(d.value || u.value)) { d.value = !0; try { u.value = await Mc(l.value), E.value = { enableVoiceChat: !!((pe = u.value) != null && pe.enable_voice_chat), pollyAvailable: !!((Ge = u.value) != null && Ge.polly_enabled), usePolly: !!((re = u.value) != null && re.polly_enabled) && ud(), voiceId: ((B = u.value) == null ? void 0 : B.polly_voice_id) || "Zayd", enable_changai: !!((de = u.value) != null && de.enable_changai) }, $(), r.value.push({ type: "settings", settings: u.value }) } catch (ae) { const ve = vl(ae); r.value.push({ type: "settings", error: ve }) } finally { d.value = !1 } } } function P() { t.value = !t.value } function ee() { var te; (te = a.value) == null || te.scrollToBottom() } function V() { f.value = !f.value } function se() { const te = !E.value.usePolly; E.value = { ...E.value, usePolly: te && E.value.pollyAvailable }, cd(E.value.usePolly), $() } function fe() { y.value = !y.value } async function D(te) { n.value === "support" ? await Ue(te) : await Z(te) } async function Z(te) { var ye, ht; g.value = null, l.value === "actual" && await O(), s.value.push({ role: "user", text: te }), await St(), ee(); const C = cs({ role: "model", text: "Thinking...", cancelable: !0, isStatus: !0, statusType: "thinking" }); s.value.push(C), await St(), ee(); let pe = !1; const Ge = ad(), re = `${Ge}_${Date.now()}`, B = y.value, de = Pc(te, Ge, l.value, re, y.value), ae = `debug_${re}`; let ve = Date.now(); const ge = [], nt = G => { var h; const Ce = Date.now(), Ft = ((Ce - ve) / 1e3).toFixed(2); ve = Ce; const c = `${G.message} (${Ft}s)`; if (G.message && (ge.push(c), g.value = c), !G.done && G.message && (C.text = G.message, C.statusType = "pipeline"), G.done) { C.cancelable = !1, G.error ? (C.text = `⚠️ ${G.message || "Something failed"}`, C.isStatus = !1, C.statusType = null) : (h = G.data) != null && h.answer && (C.text = G.data.answer, C.isStatus = !1, C.statusType = null), frappe.realtime.off(ae), g.value = null; return } }; frappe.realtime.on(ae, nt), M.value = () => { pe || (pe = !0, de.cancel(), frappe.realtime.off(ae), C.isStatus = !1, C.statusType = null, C.text = "Cancelled by user.", r.value.push({ type: "cancelled", user: te, steps: [...ge] }), g.value = null, C.cancelable = !1, M.value = null) }; try { const G = await de.promise; if (G != null && G.open_report) { if (C.isStatus = !1, C.statusType = null, C.text = `Opening "${G.report_name}" report." `, r.value.push({ type: "success", steps: [...ge], final_response: G, entity_raw: G.entity_raw }), g.value = null, !G.report_name) { C.text = "Report name extraction failed.Can you ask the same question again?"; return } frappe.set_route("query-report", G.report_name, G.filters || {}); return } else if (G != null && G.create_entity) { C.isStatus = !1, C.statusType = null, C.cancelable = !1, C.text = `Opening "${G.doc}" doctype for creating Entity "${G.entity_name}" record.`, r.value.push({ type: "success", user: te, steps: [...ge], final_response: G }), g.value = null; const Ft = G.doc, c = G.entity_name || "", b = { Customer: { customer_name: c }, Supplier: { supplier_name: c }, Employee: { employee_name: c }, Item: { item_code: c, item_name: c }, Project: { project_name: c }, Lead: { lead_name: c }, Opportunity: { opportunity_name: c } }[Ft] || {}; frappe.route_options = b, frappe.set_route("Form", Ft, "new"); const k = setInterval(() => { cur_frm && cur_frm.doctype === Ft && cur_frm.is_new() && (clearInterval(k), Object.entries(b).forEach(([v, _]) => { _ && cur_frm.fields_dict[v] && (cur_frm.set_value(v, _), cur_frm.refresh_field(v)) })) }, 200); return } if (pe) return; C.cancelable = !1; const Ce = ((ye = bp(G == null ? void 0 : G.Bot)) == null ? void 0 : ye.trim()) || "No response."; C.isStatus = !1, C.statusType = null, C.text = Ce, r.value.push({ type: "success", user: te, steps: [...ge], final_response: G }), g.value = null } catch (G) { if (pe) return; frappe.realtime.off(ae), C.cancelable = !1, C.isStatus = !1, C.statusType = null; const Ce = vl(G); g.value = null, r.value.push({ type: "failed", user: te, steps: [...ge], error: Ce }), (G == null ? void 0 : G.code) === "ERR_NETWORK_CHANGED" || (ht = G == null ? void 0 : G.message) != null && ht.includes("ERR_NETWORK_CHANGED") ? (C.isStatus = !1, C.statusType = null, C.text = "⚠️ Network error. Please check your connection and try again.") : (C.isStatus = !1, C.statusType = null, C.text = "⚠️ Something went wrong. Please try again.") } finally { frappe.realtime.off(ae), pe || (M.value = null) } await St(), ee() } function Se() { var te, C; if (n.value === "support") { (te = m.value) == null || te.call(m); return } (C = M.value) == null || C.call(M) } async function Ue(te) { o.value.push({ role: "user", text: te }), await St(), ee(); const C = cs({ role: "model", text: "Sending to support...", isStatus: !0, statusType: "support" }); o.value.push(C), await St(), ee(); let pe = !1; const Ge = Ic(te, l.value); m.value = () => { pe || (pe = !0, Ge.cancel(), C.isStatus = !1, C.statusType = null, C.text = "Cancelled by user.", m.value = null) }; try { const re = await Ge.promise; if (pe) return; C.isStatus = !1, C.statusType = null, C.text = re ? wl(re) : "Support request sent successfully." } catch { if (pe) return; C.isStatus = !1, C.statusType = null, C.text = "⚠️ Failed to reach support. Please try again." } finally { pe || (m.value = null) } await St(), ee() } return zn(() => { typeof window != "undefined" && window.addEventListener("changai-tts-provider", z), l.value === "actual" && O() }), ys(() => { typeof window != "undefined" && window.removeEventListener("changai-tts-provider", z) }), (te, C) => (F(), W(He, null, [E.value.enable_changai ? (F(), Et(ac, { key: 0, isOpen: t.value, onToggle: P }, null, 8, ["isOpen"])) : qe("", !0), Oe(ld, { ref_key: "popupRef", ref: a, isOpen: t.value, activeTab: n.value, "onUpdate:activeTab": C[0] || (C[0] = pe => n.value = pe), chatHistory: s.value, debugLogs: r.value, currentDebug: g.value, supportHistory: o.value, autoReadEnabled: f.value, ttsConfig: E.value, activeTtsProvider: x.value, settings: u.value, isAwaitingChatResponse: H.value, isAwaitingSupportResponse: Y.value, debugEnabled: i.value, sendNonERPtoaiEnabled: y.value, onToggleDebug: C[1] || (C[1] = pe => i.value = !i.value), onClose: C[2] || (C[2] = pe => t.value = !1), onSubmit: D, onCancelResponse: Se, onToggleAutoRead: V, onTogglePollyPreference: se, onToggleSendNonERP: fe }, null, 8, ["isOpen", "activeTab", "chatHistory", "debugLogs", "currentDebug", "supportHistory", "autoReadEnabled", "ttsConfig", "activeTtsProvider", "settings", "isAwaitingChatResponse", "isAwaitingSupportResponse", "debugEnabled", "sendNonERPtoaiEnabled"])], 64)) } }; function pd() { const e = document.querySelector('link[href*="/assets/changai/dist/changai-chatbot.css"]'); if (e != null && e.href) return e.href; const t = Array.from(document.scripts).find(n => { var s; return (s = n.src) == null ? void 0 : s.includes("/assets/changai/dist/changai-chatbot.js") }); return t != null && t.src ? t.src.replace(/changai-chatbot\.js(\?.*)?$/, "changai-chatbot.css$1") : null } function dd(e) { const t = pd(); if (!t) { const n = Array.from(document.querySelectorAll("style[data-vite-dev-id]")); return n.length && n.forEach(s => { const r = document.createElement("style"); r.dataset.changaiShadowDevStyle = "1", r.textContent = s.textContent || "", e.appendChild(r) }), Promise.resolve() } return e.querySelector('link[data-changai-shadow-style="1"]') ? Promise.resolve() : new Promise(n => { const s = document.createElement("link"); s.rel = "stylesheet", s.href = t, s.dataset.changaiShadowStyle = "1", s.onload = () => n(), s.onerror = () => n(), e.appendChild(s), setTimeout(n, 1200) }) } async function kl() { if (document.getElementById("changai-chatbot-host")) return; const e = document.createElement("div"); e.id = "changai-chatbot-host", document.body.appendChild(e); const t = e.attachShadow({ mode: "open" }); await dd(t); const n = document.createElement("div"); n.id = "changai-chatbot-root", t.appendChild(n), nc(fd).mount(n); function s(r) { r.stopPropagation() } n.addEventListener("keydown", s), n.addEventListener("keyup", s), n.addEventListener("keypress", s) } document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", kl) : kl() +})(); diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 03162eb..bc4be27 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -99,6 +99,10 @@ function togglePollyPreference() { function sendNonErpToAI() { sendNonERPtoaiEnabled.value = !sendNonERPtoaiEnabled.value + localStorage.setItem( + 'sendNonERPtoaiEnabled', + sendNonERPtoaiEnabled.value + ) } async function handleSubmit(message) { if (activeTab.value === 'support') { @@ -126,7 +130,9 @@ async function handleChatSubmit(message) { let cancelled = false const chatId = getOrCreateChatId() const requestId = `${chatId}_${Date.now()}` - const sendNonErptoAI = sendNonERPtoaiEnabled.value + const sendNonERPtoaiEnabled = ref( + localStorage.getItem('sendNonERPtoaiEnabled') === 'true' + ) console.log('sendNonErptoAI value being sent:', sendNonErptoAI, typeof sendNonErptoAI) const eventName = `debug_${requestId}` frappe.realtime.on(eventName, onPipelineUpdate) diff --git a/frontend/src/utils/frappe.js b/frontend/src/utils/frappe.js index 8c39f10..cd1ff6a 100644 --- a/frontend/src/utils/frappe.js +++ b/frontend/src/utils/frappe.js @@ -2,9 +2,9 @@ const IS_DEV = import.meta.env.DEV export const API = { PIPELINE: 'changai.changai.api.v2.text2sql_pipeline_v2.run_text2sql_pipeline', - SUPPORT: 'changai.changai.api.v2.text2sql_pipeline_v2.support_bot', - SETTINGS: 'changai.changai.api.v2.text2sql_pipeline_v2.get_frontend_settings', - TTS: 'changai.changai.api.v2.text2sql_pipeline_v2.synthesize_tts', + SUPPORT: 'changai.changai.api.v2.helpdesk_api.support_bot', + SETTINGS: 'changai.changai.api.v2.schema_utils.get_frontend_settings', + TTS: 'changai.changai.api.v2.tts.synthesize_tts', } export function frappeCall(method, args = {}, mode = 'actual') {