From 3d2e4e770581e5da51699dd041b8883a4bfe0115 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Fri, 21 Nov 2025 21:59:54 +0800
Subject: [PATCH 01/98] Restore: feature/service
This branch was previously covered by a revert for misoperation of PR. Now it should be restored for the following developing. - Sincere apology from Kiramei.
---
core/Baas_thread.py | 7 +-
core/config/config_set.py | 31 +-
core/config/default_config.py | 10 +-
core/ocr/baas_ocr_client/server_installer.py | 151 +++---
core/scheduler.py | 1 +
core/utils.py | 48 +-
deploy/installer/installer.py | 47 +-
deploy/installer/toml_config.py | 43 ++
gui/components/expand/baasUpdateConfig.py | 80 +++-
main.py | 13 +-
main.service.py | 64 +++
requirements-linux.txt | 2 +-
requirements.service.txt | 35 ++
requirements.txt | 2 +-
service.example.py | 12 +-
service/__init__.py | 4 +
service/app.py | 364 ++++++++++++++
service/broadcast.py | 48 ++
service/config_manager.py | 479 +++++++++++++++++++
service/context.py | 51 ++
service/diff.py | 136 ++++++
service/encryption.py | 71 +++
service/logging.py | 144 ++++++
service/messages.py | 64 +++
service/runtime.py | 342 +++++++++++++
service/utils/__init__.py | 12 +
service/utils/config_ops.py | 147 ++++++
service/utils/update.py | 300 ++++++++++++
28 files changed, 2534 insertions(+), 174 deletions(-)
create mode 100644 main.service.py
create mode 100644 requirements.service.txt
create mode 100644 service/__init__.py
create mode 100644 service/app.py
create mode 100644 service/broadcast.py
create mode 100644 service/config_manager.py
create mode 100644 service/context.py
create mode 100644 service/diff.py
create mode 100644 service/encryption.py
create mode 100644 service/logging.py
create mode 100644 service/messages.py
create mode 100644 service/runtime.py
create mode 100644 service/utils/__init__.py
create mode 100644 service/utils/config_ops.py
create mode 100644 service/utils/update.py
diff --git a/core/Baas_thread.py b/core/Baas_thread.py
index 32078853b..1b00b82a6 100644
--- a/core/Baas_thread.py
+++ b/core/Baas_thread.py
@@ -71,7 +71,7 @@
class Baas_thread:
- def __init__(self, config, logger_signal=None, button_signal=None, update_signal=None, exit_signal=None):
+ def __init__(self, config, logger_signal=None, button_signal=None, update_signal=None, exit_signal=None, **kwargs):
self.project_dir = os.path.abspath(os.path.dirname(__file__))
self.project_dir = os.path.dirname(self.project_dir)
self.u2_client = None
@@ -100,7 +100,7 @@ def __init__(self, config, logger_signal=None, button_signal=None, update_signal
self.task_finish_to_main_page = False
self.static_config = ConfigSet.static_config
self.ocr = None
- self.logger = utils.Logger(logger_signal)
+ self.logger = utils.Logger(logger_signal, jsonify=kwargs.get("jsonify", False))
self.last_refresh_u2_time = 0
self.latest_img_array = None
self.total_assault_difficulty_names = ["NORMAL", "HARD", "VERYHARD", "HARDCORE", "EXTREME", "INSANE", "TORMENT"]
@@ -693,6 +693,9 @@ def init_config(self):
except Exception as e:
self.logger.error("Config initialization failed")
self.logger.error(e.__str__())
+
+ import traceback
+ traceback.print_exc()
return False
def swipe(self, fx, fy, tx, ty, duration=None, post_sleep_time=0):
diff --git a/core/config/config_set.py b/core/config/config_set.py
index 0878af764..588bb4d2f 100644
--- a/core/config/config_set.py
+++ b/core/config/config_set.py
@@ -3,8 +3,6 @@
import re
from core.config.generated_user_config import Config
from core.config.generated_static_config import StaticConfig
-from gui.util.customized_ui import BoundComponent
-from gui.util.translator import baasTranslator as bt
from dataclasses import asdict
@@ -54,20 +52,32 @@ def get_server_mode(server):
return 'JP'
def get(self, key, default=None):
- self._init_config()
- value = getattr(self.config, key, default)
- return bt.tr('ConfigTranslation', value)
+ try:
+ from gui.util.translator import baasTranslator as bt
+ self._init_config()
+ value = getattr(self.config, key, default)
+ return bt.tr('ConfigTranslation', value)
+ except ModuleNotFoundError:
+ self._init_config()
+ return getattr(self.config, key, default)
def has(self, key):
self._init_config()
return hasattr(self.config, key)
def set(self, key, value):
- self._init_config()
- value = bt.undo(value)
- setattr(self.config, key, value)
- self.save()
- self.dynamic_update(key)
+ try:
+ from gui.util.translator import baasTranslator as bt
+ self._init_config()
+ value = bt.undo(value)
+ setattr(self.config, key, value)
+ self.save()
+ self.dynamic_update(key)
+ except ModuleNotFoundError:
+ self._init_config()
+ setattr(self.config, key, value)
+ self.save()
+ self.dynamic_update(key)
def update(self, key, value):
self.set(key, value)
@@ -114,6 +124,7 @@ def inject(self, component, string_rule, attribute="setText"):
:param attribute: Attribute to inject (default is setText)
:return: BoundComponent, which can be ignored
"""
+ from gui.util.customized_ui import BoundComponent
bounded = BoundComponent(component, string_rule, self, attribute)
self.inject_config_list.extend(re.findall(r'{(.*?)}', string_rule))
self.inject_comp_list.append(bounded)
diff --git a/core/config/default_config.py b/core/config/default_config.py
index 71fff346c..bb77015f4 100644
--- a/core/config/default_config.py
+++ b/core/config/default_config.py
@@ -338,7 +338,7 @@
DEFAULT_CONFIG = """
{
- "name": "新的配置",
+ "name": "New Config",
"purchase_arena_ticket_times": "0",
"screenshot_interval": "0.3",
"autostart": false,
@@ -3628,14 +3628,6 @@
"JP_name": "ホシノ(臨戦)",
"JP_implementation": true
},
- {
- "CN_name": "星野(临战)",
- "CN_implementation": false,
- "Global_name": "Hoshino (Battle)",
- "Global_implementation": false,
- "JP_name": "ホシノ(臨戦)",
- "JP_implementation": true
- },
{
"CN_name": "萌绘(泳装)",
"CN_implementation": false,
diff --git a/core/ocr/baas_ocr_client/server_installer.py b/core/ocr/baas_ocr_client/server_installer.py
index fb8f8cb44..3768c630b 100644
--- a/core/ocr/baas_ocr_client/server_installer.py
+++ b/core/ocr/baas_ocr_client/server_installer.py
@@ -13,90 +13,121 @@
import shutil
import os
-from core.exception import OcrInternalError
-from dulwich import porcelain
-from dulwich.repo import Repo
import platform
+import pygit2
+from pygit2 import Commit
+
+from core.exception import OcrInternalError
+from pygit2.enums import ResetMode
-if sys.platform not in ['win32', 'linux', 'darwin']:
+if sys.platform not in ["win32", "linux", "darwin"]:
raise Exception("Ocr Unsupported platform " + sys.platform)
OCR_SERVER_PREBUILD_URL = "https://gitee.com/pur1fy/baas_-cpp_prebuild.git"
SERVER_INSTALLER_DIR_PATH = os.path.dirname(os.path.abspath(__file__))
-SERVER_BIN_DIR = os.path.join(SERVER_INSTALLER_DIR_PATH, 'bin')
-
-branch = {
- 'win32': {
- 'amd64': 'windows-x64',
- },
- 'linux': {
- 'x86_64': 'linux-x64',
- },
- 'darwin': {
- 'arm64': 'macos-arm64',
- },
+SERVER_BIN_DIR = os.path.join(SERVER_INSTALLER_DIR_PATH, "bin")
+
+branch_map = {
+ "win32": {"amd64": "windows-x64"},
+ "linux": {"x86_64": "linux-x64"},
+ "darwin": {"arm64": "macos-arm64"},
}
-branch = branch[sys.platform]
+arch_map = branch_map[sys.platform]
arch = platform.machine().lower()
-if arch not in branch:
+if arch not in arch_map:
raise Exception("Unsupported machine architecture " + arch)
-branch = branch[arch]
+branch = arch_map[arch]
def check_git(logger):
- if not os.path.exists(SERVER_BIN_DIR + '/.git'):
+ """
+ 检查并更新 OCR Server 的本地 git 仓库
+ """
+ git_dir = os.path.join(SERVER_BIN_DIR, ".git")
+ if not os.path.exists(git_dir):
+ clone_repo(logger)
+ return
+
+ logger.info("Ocr Server Update check.")
+ try:
+ repo = pygit2.Repository(SERVER_BIN_DIR)
+ local_sha = str(repo.head.target)
+ except Exception:
+ logger.warning("Git Repo corrupted, remove .git folder and reinstall.")
+ shutil.rmtree(git_dir, ignore_errors=True)
clone_repo(logger)
+ return
+
+ try:
+ git = pygit2.Repository()
+ remote = git.remotes.create_anonymous(OCR_SERVER_PREBUILD_URL)
+ refs = {head.get("name"): head.get("oid") for head in remote.ls_remotes()}
+ remote_sha = str(refs.get(f"refs/heads/{branch}", None))
+ except Exception as e:
+ raise OcrInternalError(f"Failed to fetch remote info: {e}")
+
+ if not remote_sha:
+ logger.warning(f"Remote branch '{branch}' not found.")
+ return
+
+ logger.info(f"remote_sha: {remote_sha}")
+ logger.info(f"local_sha : {local_sha}")
+
+ if local_sha == remote_sha:
+ logger.info("Ocr Server No updates available.")
+ return
+
+ logger.info("Pulling updates from the remote repository...")
+
+ try:
+ remote_obj = (
+ repo.remotes["origin"]
+ if "origin" in list(repo.remotes.names())
+ else repo.remotes.create("origin", OCR_SERVER_PREBUILD_URL)
+ )
+ refspec = f"refs/heads/{branch}:refs/remotes/origin/{branch}"
+ remote_obj.fetch(refspecs=[refspec])
+ remote_commit = repo.revparse_single(f"refs/remotes/origin/{branch}")
+ if not isinstance(remote_commit, Commit):
+ remote_commit = repo[remote_commit.target]
+ repo.reset(remote_commit.id, ResetMode.HARD)
+ repo.checkout_tree(remote_commit.tree)
+ updated_sha = str(repo.head.target)
+
+ except KeyError as ke:
+ logger.error(f"Branch {branch} not found in remote repository.")
+ raise OcrInternalError(f"Remote branch '{branch}' does not exist: {ke}")
+
+ except Exception as e:
+ logger.error("Failed to update the BAAS_ocr_server.")
+ raise OcrInternalError(f"Update failed: {e}")
+
+ if updated_sha == remote_sha:
+ logger.info("Ocr Server Update success.")
else:
- logger.info("Ocr Server Update check.")
- try:
- repo = Repo(SERVER_BIN_DIR)
- # Get local SHA
- local_sha = repo.head().decode('ascii')
- except Exception:
- logger.warning("Git Repo corrupted, remove .git folder and reinstall.")
- shutil.rmtree(SERVER_BIN_DIR + '/.git')
- clone_repo(logger)
- return
- # Get remote SHA
- remote_refs = porcelain.ls_remote(OCR_SERVER_PREBUILD_URL)
- remote_sha = remote_refs.get(b'refs/heads/' + branch.encode('ascii')).decode('ascii')
-
- logger.info(f"remote_sha: {remote_sha}")
- logger.info(f"local_sha : {local_sha}")
-
- if local_sha == remote_sha:
- logger.info("Ocr Server No updates available.")
- else:
- logger.info("Pulling updates from the remote repository...")
- # Reset the local repository to the state of the remote repository
- porcelain.reset(repo, mode='hard')
- # Pull the latest changes from the remote repository
- for i in range(1, 4):
- try:
- porcelain.pull(repo, OCR_SERVER_PREBUILD_URL, branch, protocol_version=0)
- break
- except Exception as e:
- if i == 3:
- raise OcrInternalError("Failed to update the BAAS_ocr_server. Please check your network")
- logger.error(f"Failed to update BAAS_ocr_server, retrying... {i}")
- logger.error(e)
- updated_local_sha = repo.head().decode('ascii')
- if updated_local_sha == remote_sha:
- logger.info("Ocr Server Update success.")
- else:
- logger.warning("Failed to update the BAAS_ocr_server, please check your network.")
+ logger.warning("Failed to update the BAAS_ocr_server, please check your network.")
def clone_repo(logger):
logger.info("Installing Ocr Server, please hang on...")
+
+ if os.path.exists(SERVER_BIN_DIR) and os.listdir(SERVER_BIN_DIR):
+ logger.warning("Target directory not empty, removing old files...")
+ shutil.rmtree(SERVER_BIN_DIR)
+
for i in range(1, 4):
try:
- porcelain.clone(OCR_SERVER_PREBUILD_URL, SERVER_BIN_DIR, branch=branch)
+ pygit2.clone_repository(
+ OCR_SERVER_PREBUILD_URL,
+ SERVER_BIN_DIR,
+ checkout_branch=branch,
+ )
break
except Exception as e:
if i == 3:
raise OcrInternalError("Failed to install the BAAS_ocr_server. Please check your network")
logger.error(f"Failed to install BAAS_ocr_server, retrying... {i}")
- logger.error(e.__str__())
+ logger.error(str(e))
+
logger.info("Ocr Server Install success.")
diff --git a/core/scheduler.py b/core/scheduler.py
index b02db3b1a..32c59f96d 100644
--- a/core/scheduler.py
+++ b/core/scheduler.py
@@ -118,6 +118,7 @@ def update_valid_task_queue(self):
_valid_event = sorted(_valid_event, key=lambda x: x['priority']) # sort by priority
self._valid_task_queue = []
+ self._waitingTaskDisplayQueue = []
for i in range(0, len(_valid_event)):
self._waitingTaskDisplayQueue.append(_valid_event[i]['event_name'])
thisTask = {
diff --git a/core/utils.py b/core/utils.py
index 9775aa5c0..d82494fad 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -3,6 +3,19 @@
import threading
from typing import Union
from datetime import datetime, timedelta, timezone
+import queue
+
+# Status Text: INFO, WARNING, ERROR, CRITICAL
+STATUS_TERMINAL = [' INFO', ' WARNING', ' ERROR', 'CRITICAL']
+STATUS_HTML = [' INFO', ' WARNING', ' ERROR', 'CRITICAL']
+
+# Status Color: Blue, Orange, Red, Purple
+STATUS_COLOR = ['#2d8cf0', '#f90', '#ed3f14', '#3e0480']
+
+# Status HTML: status
+STATUS_HTML_EMB = [
+ f'{status}'
+ for _color, status in zip(STATUS_COLOR, STATUS_HTML)]
def delay(wait=1):
@@ -25,23 +38,28 @@ def call_it():
return decorator
+
def detach(func):
def wrapper(*args, **kwargs):
threading.Thread(target=func, args=args[:-1], kwargs=kwargs).start()
+
return wrapper
+
class Logger:
"""
Logger class for logging
"""
- def __init__(self, logger_signal):
+ def __init__(self, logger_signal, jsonify=False):
"""
:param logger_signal: Logger Box signal
"""
# Init logger box signal, logs and logger
# logger box signal is used to output log to logger box
self.logs = ""
+ self.jsonify = jsonify
+ self.log_collector = queue.Queue()
self.logger_signal = logger_signal
self.logger = logging.getLogger("BAAS_Logger")
formatter = logging.Formatter("%(levelname)8s |%(asctime)20s | %(message)s ")
@@ -63,29 +81,31 @@ def __out__(self, message: str, level: int = 1, raw_print=False) -> None:
self.logger_signal.emit(message)
return
+ if self.jsonify:
+ self.log_collector.put({
+ "time": datetime.now(),
+ "level": level,
+ "message": message
+ })
+ print(
+ f'{STATUS_TERMINAL[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}')
+ return
+
while len(logging.root.handlers) > 0:
logging.root.handlers.pop()
- # Status Text: INFO, WARNING, ERROR, CRITICAL
- status = [' INFO', ' WARNING', ' ERROR', 'CRITICAL']
- # Status Color: Blue, Orange, Red, Purple
- statusColor = ['#2d8cf0', '#f90', '#ed3f14', '#3e0480']
- # Status HTML: status
- statusHtml = [
- f'{status}'
- for _color, status in zip(statusColor, status)]
# If logger box is not None, output log to logger box
# else output log to console
if self.logger_signal is not None:
message = message.replace('\n', '
').replace(' ', ' ')
adding = (f'''
-
- {statusHtml[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}
+
+ {STATUS_HTML_EMB[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}
''')
self.logs += adding
self.logger_signal.emit(adding)
else:
- print(f'{statusHtml[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}')
+ print(f'{STATUS_HTML_EMB[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}')
def info(self, message: str) -> None:
"""
@@ -232,6 +252,7 @@ def is_nearby_group(coord, group, abs_x=10, abs_y=10):
return True
return False
+
def get_nearest_hour(target_hour):
now = datetime.now(timezone.utc)
current_hour = now.hour
@@ -241,7 +262,7 @@ def get_nearest_hour(target_hour):
hour_delta = diff - 24
else:
hour_delta = diff
- else: # target_hour < current_hour
+ else: # target_hour < current_hour
diff = current_hour - target_hour
if diff > 12:
hour_delta = 24 - diff
@@ -250,4 +271,3 @@ def get_nearest_hour(target_hour):
nearest_time = (now + timedelta(hours=hour_delta)).replace(minute=0, second=0, microsecond=0)
return nearest_time
-
diff --git a/deploy/installer/installer.py b/deploy/installer/installer.py
index eac30c1c7..13fcc96cf 100644
--- a/deploy/installer/installer.py
+++ b/deploy/installer/installer.py
@@ -51,15 +51,14 @@
import tomli_w
from copy import deepcopy
from alive_progress import alive_bar
-# from dulwich import porcelain
-# from dulwich.repo import Repo
+
from easydict import EasyDict as eDict
from halo import Halo
from loguru import logger
from pygit2 import clone_repository, Repository, RemoteCallbacks, GIT_RESET_HARD, GitError
-from toml_config import TOML_Config
+from toml_config import TOML_Config, DEFAULT_SETTINGS
from mirrorc_update.mirrorc_updater import MirrorC_Updater
from const import GetShaMethod, get_remote_sha_methods, REPO_BRANCH
@@ -72,48 +71,6 @@
# ==================== Default Settings =====================
-DEFAULT_SETTINGS = {
- "General": {
- "mirrorc_cdk": "",
- "current_BAAS_version": "",
- "current_BAAS_Cpp_version": "",
- "get_remote_sha_method": "",
- "dev": False,
- "refresh": False,
- "launch": False,
- "force_launch": False,
- "internal_launch": False,
- "no_build": True,
- "debug": False,
- "use_dynamic_update": False,
- "source_list": [
- "https://pypi.tuna.tsinghua.edu.cn/simple",
- "https://mirrors.ustc.edu.cn/pypi/web/simple",
- "https://mirrors.aliyun.com/pypi/simple",
- "https://pypi.doubanio.com/simple",
- "https://mirrors.huaweicloud.com/repository/pypi/simple",
- "https://mirrors.cloud.tencent.com/pypi/simple",
- "https://mirrors.163.com/pypi/simple",
- "https://pypi.python.org/simple",
- "https://pypi.org/simple",
- ],
- "package_manager": "pip",
- "runtime_path": "default",
- "linux_pwd": "",
- },
- "URLs": {
- "REPO_URL_HTTP": "https://gitee.com/pur1fy/blue_archive_auto_script.git",
- "GET_PIP_URL": "https://gitee.com/pur1fy/blue_archive_auto_script_assets/raw/master/get-pip.py",
- "GET_UPX_URL": "https://ghp.ci/https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-win64.zip",
- "GET_ENV_PATCH_URL": "https://gitee.com/pur1fy/blue_archive_auto_script_assets/raw/master/env_patch.zip",
- "GET_PYTHON_URL": "https://gitee.com/pur1fy/blue_archive_auto_script_assets/raw/master/python-3.9.13-embed-amd64.zip",
- },
- "Paths": {
- "BAAS_ROOT_PATH": "",
- "TMP_PATH": "tmp",
- "TOOL_KIT_PATH": "toolkit",
- },
-}
repo = None
diff --git a/deploy/installer/toml_config.py b/deploy/installer/toml_config.py
index 38e2ad992..aec02def8 100644
--- a/deploy/installer/toml_config.py
+++ b/deploy/installer/toml_config.py
@@ -1,6 +1,49 @@
import os
import tomli_w
+DEFAULT_SETTINGS = {
+ "General": {
+ "mirrorc_cdk": "",
+ "current_BAAS_version": "",
+ "current_BAAS_Cpp_version": "",
+ "get_remote_sha_method": "",
+ "dev": False,
+ "refresh": False,
+ "launch": False,
+ "force_launch": False,
+ "internal_launch": False,
+ "no_build": True,
+ "debug": False,
+ "use_dynamic_update": False,
+ "source_list": [
+ "https://pypi.tuna.tsinghua.edu.cn/simple",
+ "https://mirrors.ustc.edu.cn/pypi/web/simple",
+ "https://mirrors.aliyun.com/pypi/simple",
+ "https://pypi.doubanio.com/simple",
+ "https://mirrors.huaweicloud.com/repository/pypi/simple",
+ "https://mirrors.cloud.tencent.com/pypi/simple",
+ "https://mirrors.163.com/pypi/simple",
+ "https://pypi.python.org/simple",
+ "https://pypi.org/simple",
+ ],
+ "package_manager": "pip",
+ "runtime_path": "default",
+ "linux_pwd": "",
+ },
+ "URLs": {
+ "REPO_URL_HTTP": "https://gitee.com/pur1fy/blue_archive_auto_script.git",
+ "GET_PIP_URL": "https://gitee.com/pur1fy/blue_archive_auto_script_assets/raw/master/get-pip.py",
+ "GET_UPX_URL": "https://ghp.ci/https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-win64.zip",
+ "GET_ENV_PATCH_URL": "https://gitee.com/pur1fy/blue_archive_auto_script_assets/raw/master/env_patch.zip",
+ "GET_PYTHON_URL": "https://gitee.com/pur1fy/blue_archive_auto_script_assets/raw/master/python-3.9.13-embed-amd64.zip",
+ },
+ "Paths": {
+ "BAAS_ROOT_PATH": "",
+ "TMP_PATH": "tmp",
+ "TOOL_KIT_PATH": "toolkit",
+ },
+}
+
try:
import tomllib
except ModuleNotFoundError:
diff --git a/gui/components/expand/baasUpdateConfig.py b/gui/components/expand/baasUpdateConfig.py
index e2a9842db..6ee585025 100644
--- a/gui/components/expand/baasUpdateConfig.py
+++ b/gui/components/expand/baasUpdateConfig.py
@@ -1,9 +1,11 @@
import binascii
+import tempfile
import threading
import time
import webbrowser
+from pathlib import Path
-import dulwich
+import pygit2
import requests
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QColor
@@ -35,7 +37,7 @@ def run(self):
elif self.method == GetShaMethod.MIRRORC_API:
success, sha_value = self.mirrorc_api_get_latest_sha()
elif self.method == GetShaMethod.PYGIT2:
- success, sha_value = self.dulwich_get_latest_sha(self.config)
+ success, sha_value = self.pygit2_get_latest_sha(self.config)
else:
success = False
sha_value = self.tr("未知方法")
@@ -71,31 +73,53 @@ def mirrorc_api_get_latest_sha():
return False, str(e)
@staticmethod
- def dulwich_get_latest_sha(data):
+ def pygit2_get_latest_sha(data):
url = data["url"]
branch = data["branch"]
target_ref = f"refs/heads/{branch}"
try:
- remote_refs = dulwich.porcelain.ls_remote(url)
- for ref_name, sha in remote_refs.items():
- decoded_ref = ref_name.decode("utf-8")
+ # 创建一个临时 bare 仓库
+ tmpdir = tempfile.mkdtemp(prefix="pygit2-remote-")
+ repo = pygit2.init_repository(tmpdir, bare=True)
+
+ # 添加远程仓库
+ try:
+ remote = repo.remotes["origin"]
+ except KeyError:
+ remote = repo.remotes.create("origin", url)
+
+ # 拉取远程引用(不下载对象)
+ remote.fetch() # 轻量 fetch,默认不会 checkout
+
+ # 获取远程引用表
+ remote_refs = remote.ls_remotes()
+
+ # 遍历匹配分支引用
+ for ref_name, oid in remote_refs:
+ decoded_ref = ref_name
if decoded_ref == target_ref:
- if len(sha) == 20:
- return True, binascii.hexlify(sha).decode("utf-8")
- try:
- hex_str = sha.decode("utf-8")
- if len(hex_str) == 40:
- return True, hex_str
- except:
- pass
- return True, binascii.hexlify(sha[:20]).decode("utf-8")
- ref = f"refs/heads/{branch}",
- sha = remote_refs[ref.encode("utf-8")]
- return True, binascii.hexlify(sha[:20]).decode("utf-8")
+ sha = str(oid)
+ return True, sha
+
+ # 如果没找到直接尝试 refs/remotes/origin/
+ ref_full = f"refs/remotes/origin/{branch}"
+ if ref_full in repo.references:
+ oid = repo.references[ref_full].target
+ sha = str(oid)
+ return True, sha
+
+ return False, f"Branch '{branch}' not found in remote."
+
except Exception as e:
return False, str(e)
-
+ finally:
+ # 删除临时目录
+ try:
+ import shutil
+ shutil.rmtree(tmpdir)
+ except Exception:
+ pass
class MirrorCCDKTestThread(QThread):
finished = pyqtSignal(RequestReturn, bool)
@@ -349,10 +373,14 @@ def _init_data_and_state(self):
def _get_local_version(self):
self._BAAS_local_version_sha = self.config.get("General.current_BAAS_version", None)
method = "setup.toml"
+
if not self._BAAS_local_version_sha:
try:
- self._repo = dulwich.repo.Repo(".")
- self._BAAS_local_version_sha = self._repo.head().decode("utf-8")
+ repo_path = Path.cwd()
+ repo = pygit2.Repository(repo_path)
+ self._repo = repo
+
+ self._BAAS_local_version_sha = str(repo.head.target)
method = ".git"
except Exception:
method = None
@@ -360,7 +388,13 @@ def _get_local_version(self):
if self._BAAS_local_version_sha:
sha_display = self._BAAS_local_version_sha
self._BAAS_local_version_label.setText(sha_display)
- method_text = f"({self.tr('从 setup.toml 读取')})" if method == "setup.toml" else f"({self.tr('从 .git 读取')})"
+
+ if method == "setup.toml":
+ method_text = f"({self.tr('从 setup.toml 读取')})"
+ elif method == ".git":
+ method_text = f"({self.tr('从 .git 读取')})"
+ else:
+ method_text = ""
self._BAAS_local_version_method_label.setText(method_text)
else:
self._BAAS_local_version_label.setText(self.tr("无法获取"))
@@ -581,7 +615,7 @@ def _detect_update_method_thread(self):
method_type = config["method"]
is_ok, sha = (TestGetRemoteShaMethodWorker.github_api_get_latest_sha(config)
if method_type == GetShaMethod.GITHUB_API
- else TestGetRemoteShaMethodWorker.dulwich_get_latest_sha(config))
+ else TestGetRemoteShaMethodWorker.pygit2_get_latest_sha(config))
if is_ok:
self._BAAS_remote_version_sha = sha
self._BAAS_remote_version_get_method = "git"
diff --git a/main.py b/main.py
index f652d6844..06509708c 100644
--- a/main.py
+++ b/main.py
@@ -1,5 +1,6 @@
import json
import os
+import traceback
from core.ocr import ocr
from core.utils import Logger
@@ -8,21 +9,23 @@
from core.ocr.baas_ocr_client.server_installer import check_git
class Main:
- def __init__(self, logger_signal=None, ocr_needed=None):
+ def __init__(self, logger_signal=None, ocr_needed=None, **kwargs):
self.ocr_needed = ocr_needed
self.ocr = None
- self.logger = Logger(logger_signal)
+ self.logger = Logger(logger_signal, jsonify=kwargs.get("jsonify", False))
self.project_dir = os.path.abspath(os.path.dirname(__file__))
self.logger.info(self.project_dir)
- self.init_all_data()
+ if not kwargs.get("lazy_data", False):
+ self.init_all_data()
self.threads = {}
def init_all_data(self):
if not self.init_ocr():
self.logger.error("Ocr Init Incomplete Please restart .")
- return
+ return False
self.init_static_config()
self.logger.info("-- All Data Initialization Complete Script ready--")
+ return True
def init_ocr(self):
try:
@@ -70,7 +73,7 @@ def init_static_config(self):
return True
except Exception as e:
self.logger.error("Static Config initialization failed")
- self.logger.error(e.__str__())
+ self.logger.error(traceback.format_exc())
return False
def operate_dict(self, dic):
diff --git a/main.service.py b/main.service.py
new file mode 100644
index 000000000..41272bee4
--- /dev/null
+++ b/main.service.py
@@ -0,0 +1,64 @@
+import argparse
+import os
+
+import uvicorn
+
+DEFAULT_HOST = os.getenv("BAAS_SERVICE_HOST", "127.0.0.1")
+DEFAULT_PORT = int(os.getenv("BAAS_SERVICE_PORT", "8190"))
+
+import logging, re
+
+ANSI_ESCAPE = re.compile(r'\x1b\[([0-9;]*[mGKH])')
+
+STATUS_TERMINAL = {
+ logging.INFO: " INFO",
+ logging.WARNING: " WARNING",
+ logging.ERROR: " ERROR",
+ logging.CRITICAL: "CRITICAL",
+}
+
+class PlainFormatter(logging.Formatter):
+ def format(self, record):
+ level = STATUS_TERMINAL.get(record.levelno, " INFO")
+ log_fmt = f"{level} | %(asctime)s | %(message)s"
+ formatter = logging.Formatter(log_fmt, "%Y-%m-%d %H:%M:%S")
+ output = formatter.format(record)
+
+ return ANSI_ESCAPE.sub('', output)
+
+
+handler = logging.StreamHandler()
+handler.setFormatter(PlainFormatter())
+
+root = logging.getLogger()
+root.setLevel(logging.INFO)
+root.handlers = [handler]
+
+
+
+
+def parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(description="Start BAAS service mode backend")
+ parser.add_argument("--host", default=DEFAULT_HOST, help="Host to bind (default: %(default)s)")
+ parser.add_argument("--port", type=int, default=DEFAULT_PORT, help="Port to bind (default: %(default)s)")
+ parser.add_argument("--reload", action="store_true", help="Enable auto-reload (development only)")
+ parser.add_argument("--log-level", default="info", help="Uvicorn log level")
+ return parser.parse_args()
+
+
+def main() -> None:
+ args = parse_args()
+ config = uvicorn.Config(
+ "service.app:app",
+ host=args.host,
+ port=args.port,
+ reload=args.reload,
+ log_level=args.log_level,
+ log_config=None
+ )
+ server = uvicorn.Server(config)
+ server.run()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/requirements-linux.txt b/requirements-linux.txt
index 21ca352ee..bb54ed729 100644
--- a/requirements-linux.txt
+++ b/requirements-linux.txt
@@ -3,7 +3,7 @@ PyQt5 == 5.15.11
numpy < 2.0
av == 12.0.0
imgaug
-dulwich
+pygit2
adbutils == 2.2.1
uiautomator2 == 2.16.23
opencv-python == 4.8.1.78
diff --git a/requirements.service.txt b/requirements.service.txt
new file mode 100644
index 000000000..4d90f146c
--- /dev/null
+++ b/requirements.service.txt
@@ -0,0 +1,35 @@
+# =========================== #
+# Core Utils #
+numpy < 2.0 #
+adbutils == 2.2.1 #
+uiautomator2 == 2.16.23 #
+opencv-python == 4.8.1.78 #
+# =========================== #
+
+# =========================== #
+# Process & Screenshot #
+psutil #
+requests #
+# =========================== #
+
+# =========================== #
+# Toml Config Process #
+tomli == 2.2.1 #
+tomli_w == 1.2.0 #
+# =========================== #
+
+# =========================== #
+# Steam Server Specific #
+mss == 10.0.0 #
+PyAutoGUI == 0.9.54 #
+# =========================== #
+
+# =========================== #
+# Service Specific #
+pygit2 #
+fastapi #
+uvicorn #
+websockets #
+watchfiles #
+cryptography #
+# =========================== #
diff --git a/requirements.txt b/requirements.txt
index 0b14a1540..1d9a6699f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -14,7 +14,7 @@ PyQt-Fluent-Widgets == 1.2.0
#windows installer auto build
pyinstaller
requests
-dulwich
+pygit2
psutil
tqdm
diff --git a/service.example.py b/service.example.py
index ed7acb7c9..a207e3f10 100644
--- a/service.example.py
+++ b/service.example.py
@@ -4,12 +4,16 @@
def main():
- main = Main(ocr_needed=["NUM", "Global", "JP"]) # 日服也必须要 Global,否则会崩溃
- config = ConfigSet(config_dir="jp_hoshino") # 修改为自己的配置目录名
- baas = Baas_thread(config, None, None, None)
+ main = Main(ocr_needed=["en-us", "zh-cn"], jsonify=True) # 日服也必须要 Global,否则会崩溃
+ config = ConfigSet(config_dir="default_config") # 修改为自己的配置目录名
+ baas = Baas_thread(config, None, None, None, jsonify=True)
+ # 得到Logger
+ # logger=baas.logger
+
+ # 初始化数据
baas.init_all_data()
baas.ocr = main.ocr # type: ignore
-
+ # # 应用启动
baas.thread_starter()
diff --git a/service/__init__.py b/service/__init__.py
new file mode 100644
index 000000000..5068dd815
--- /dev/null
+++ b/service/__init__.py
@@ -0,0 +1,4 @@
+from .app import app, context
+
+__all__ = ['app', 'context']
+
diff --git a/service/app.py b/service/app.py
new file mode 100644
index 000000000..4d2a88cee
--- /dev/null
+++ b/service/app.py
@@ -0,0 +1,364 @@
+from __future__ import annotations
+
+import contextlib
+import asyncio
+import os
+import secrets
+import time
+from pathlib import Path
+from typing import Any, Dict, Union
+
+from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
+from starlette.middleware.cors import CORSMiddleware
+
+from .context import ServiceContext
+from .encryption import AuthenticationError, CipherBox, HandshakeResponse, HandshakeSession
+from .messages import (
+ CommandMessage,
+ ProviderRequest,
+ SyncPatchMessage,
+ SyncPullMessage,
+)
+
+PROJECT_ROOT = Path(__file__).resolve().parent.parent
+context = ServiceContext(PROJECT_ROOT)
+
+_SHARED_SECRET: Union[str, None] = None
+
+
+def _load_shared_secret() -> str:
+ secret = os.getenv("BAAS_SERVICE_SECRET")
+ if secret:
+ return secret
+ fallback = PROJECT_ROOT / "config" / "service.secret"
+ if fallback.exists():
+ return fallback.read_text(encoding="utf-8").strip()
+ (PROJECT_ROOT / "config").mkdir(exist_ok=True)
+ token = secrets.token_hex(16)
+ fallback.write_bytes(token.encode("utf-8"))
+ return token
+
+
+@contextlib.asynccontextmanager
+async def lifespan(app: FastAPI):
+ global _SHARED_SECRET
+ _SHARED_SECRET = _load_shared_secret()
+ await context.startup()
+
+ yield
+
+ await context.shutdown()
+
+
+app = FastAPI(title="BAAS Service Mode", lifespan=lifespan)
+
+# Allow all origins
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+
+@app.get("/health")
+async def health() -> Dict[str, Any]:
+ statuses = context.runtime.current_status()
+ return {"ok": True, "statuses": statuses}
+
+
+async def _perform_handshake(websocket: WebSocket) -> tuple[HandshakeSession, CipherBox]:
+ if _SHARED_SECRET is None:
+ raise RuntimeError("Shared secret not initialised")
+ await websocket.accept()
+ session = HandshakeSession(_SHARED_SECRET)
+ challenge = session.issue_challenge()
+ await websocket.send_json({"type": "handshake", "challenge": challenge.challenge, "algorithm": challenge.algorithm})
+ raw = await websocket.receive_json()
+ response = HandshakeResponse(**raw)
+ session.verify(response.response)
+ await websocket.send_json({"type": "handshake_ok"})
+ return session, session.build_cipher()
+
+
+async def _sync_sender(websocket: WebSocket, cipher: CipherBox, queue: asyncio.Queue) -> None:
+ try:
+ while True:
+ payload = dict(await queue.get())
+ payload.setdefault("direction", "push")
+ await websocket.send_text(cipher.encrypt_json(payload))
+ except asyncio.CancelledError:
+ pass
+
+
+@app.websocket("/ws/sync")
+async def websocket_sync(websocket: WebSocket) -> None:
+ queue = None
+ sender_task = None
+ try:
+ _, cipher = await _perform_handshake(websocket)
+ queue = await context.config_manager.subscribe_updates()
+ sender_task = asyncio.create_task(_sync_sender(websocket, cipher, queue))
+ while True:
+ encrypted = await websocket.receive_text()
+ message = cipher.decrypt_json(encrypted)
+ msg_type = message.get("type")
+ if msg_type == "pull":
+ data = SyncPullMessage(**message)
+ snapshot = await context.config_manager.get_snapshot(data.resource, data.resource_id)
+ response = {
+ "type": "snapshot",
+ "resource": data.resource,
+ "resource_id": data.resource_id,
+ "timestamp": snapshot.timestamp,
+ "data": snapshot.data,
+ }
+ await websocket.send_text(cipher.encrypt_json(response))
+ elif msg_type == "patch":
+ data = SyncPatchMessage(**message)
+ await context.config_manager.apply_patch(
+ data.resource, data.resource_id, data.ops, data.timestamp, origin="frontend"
+ )
+ response = {
+ "type": "patch_ack",
+ "resource": data.resource,
+ "resource_id": data.resource_id,
+ "timestamp": data.timestamp,
+ }
+ await websocket.send_text(cipher.encrypt_json(response))
+ elif msg_type == "list":
+ snapshot = await context.config_manager.get_config_list()
+ response = {
+ "type": "config_list",
+ "timestamp": snapshot.timestamp,
+ "data": snapshot.data,
+ }
+ await websocket.send_text(cipher.encrypt_json(response))
+ else:
+ raise HTTPException(status_code=400, detail=f"Unsupported sync message: {msg_type}")
+ except (AuthenticationError, HTTPException) as exc:
+ await websocket.close(code=4401, reason=str(exc))
+ except WebSocketDisconnect:
+ pass
+ except Exception as exc:
+ import traceback
+ traceback.print_exc()
+ await websocket.close(code=1011, reason=str(exc))
+ finally:
+ if sender_task:
+ sender_task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await sender_task
+ if queue is not None:
+ context.config_manager.unsubscribe_updates(queue)
+
+
+async def _provider_sender(websocket: WebSocket, cipher: CipherBox, queue: asyncio.Queue, envelope_type: str) -> None:
+ try:
+ while True:
+ payload = await queue.get()
+ if envelope_type == "status":
+ response = {"type": envelope_type, "status": payload}
+ else:
+ response = {"type": envelope_type, "entry": payload}
+ await websocket.send_text(cipher.encrypt_json(response))
+ except asyncio.CancelledError:
+ pass
+
+
+@app.websocket("/ws/provider")
+async def websocket_provider(websocket: WebSocket) -> None:
+ log_queue = status_queue = None
+ log_task = status_task = None
+ try:
+ _, cipher = await _perform_handshake(websocket)
+ history = context.log_manager.get_history()
+ scopes = context.log_manager.get_scopes()
+ await websocket.send_text(cipher.encrypt_json({"type": "logs_full", "scopes": scopes, "entries": history}))
+ await websocket.send_text(cipher.encrypt_json({"type": "status", "status": context.runtime.current_status()}))
+ if context.runtime.is_all_data_initialized:
+ await websocket.send_text(
+ cipher.encrypt_json({"type": "status", "status": {"is_all_data_initialized": True}})
+ )
+ log_queue = await context.log_manager.subscribe()
+ status_queue = await context.runtime.subscribe_status()
+ log_task = asyncio.create_task(_provider_sender(websocket, cipher, log_queue, "log"))
+ status_task = asyncio.create_task(_provider_sender(websocket, cipher, status_queue, "status"))
+ while True:
+ encrypted = await websocket.receive_text()
+ message = cipher.decrypt_json(encrypted)
+ req_type = message.get("type")
+ if req_type == "static_request":
+ ProviderRequest(**message)
+ snapshot = await context.config_manager.get_static_snapshot()
+ await websocket.send_text(
+ cipher.encrypt_json(
+ {
+ "type": "static_snapshot",
+ "timestamp": snapshot.timestamp,
+ "data": snapshot.data,
+ }
+ )
+ )
+ elif req_type == "status_request":
+ await websocket.send_text(
+ cipher.encrypt_json({"type": "status", "status": context.runtime.current_status()}))
+ else:
+ raise HTTPException(status_code=400, detail=f"Unsupported provider message: {req_type}")
+ except (AuthenticationError, HTTPException) as exc:
+ await websocket.close(code=4401, reason=str(exc))
+ except WebSocketDisconnect:
+ pass
+ except Exception as exc:
+ await websocket.close(code=1011, reason=str(exc))
+ finally:
+ for task in (log_task, status_task):
+ if task:
+ task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await task
+ if log_queue is not None:
+ context.log_manager.unsubscribe(log_queue)
+ if status_queue is not None:
+ context.runtime.unsubscribe_status(status_queue)
+
+
+@app.websocket("/ws/trigger")
+async def websocket_trigger(websocket: WebSocket) -> None:
+ try:
+ _, cipher = await _perform_handshake(websocket)
+ while True:
+ encrypted = await websocket.receive_text()
+ message = cipher.decrypt_json(encrypted)
+ cmd = CommandMessage(**message)
+ response_payload: Dict[str, Any]
+ try:
+ if cmd.command == "start_scheduler":
+ if not cmd.config_id:
+ raise ValueError("config_id is required for start_scheduler")
+ result = await context.runtime.start_scheduler(cmd.config_id,
+ set_log=context.ensure_runtime_logger_attached)
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "stop_scheduler":
+ if not cmd.config_id:
+ raise ValueError("config_id is required for stop_scheduler")
+ result = await context.runtime.stop_scheduler(cmd.config_id)
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "solve":
+ if not cmd.config_id:
+ raise ValueError("config_id is required for solve")
+ task = cmd.payload.get("task")
+ if not task:
+ raise ValueError("task is required for solve command")
+ result = await context.runtime.solve_task(cmd.config_id, task,
+ set_log=context.ensure_runtime_logger_attached)
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command.startswith("start_"):
+ if not cmd.config_id:
+ raise ValueError(f"config_id is required for command '{cmd.command}'")
+ result = await context.runtime.solve_task(cmd.config_id, cmd.command,
+ set_log=context.ensure_runtime_logger_attached)
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command.startswith("add_config"):
+ name = cmd.payload.get("name")
+ server = cmd.payload.get("server")
+ if not server or not name:
+ raise ValueError("server and name are required for add_config")
+ result = await context.runtime.add_config(name, server)
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command.startswith("remove_config"):
+ id = cmd.payload.get("id")
+ if not id:
+ raise ValueError("id is required for remove_config")
+ result = await context.runtime.remove_config(id)
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "detect_adb":
+ result = await context.runtime.detect_adb()
+ response_payload = {"status": "ok", "data": {"addresses": result}}
+ elif cmd.command == "valid_cdk":
+ result = await context.runtime.valid_cdk(cmd.payload["cdk"])
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "test_all_sha":
+ result = await context.runtime.test_all_sha()
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "check_for_update":
+ result = await context.runtime.check_for_update()
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "update_setup_toml":
+ result = await context.runtime.check_for_update()
+ response_payload = {"status": "ok", "data": result}
+ elif cmd.command == "status":
+ response_payload = {"status": "ok", "data": context.runtime.current_status()}
+ else:
+ raise ValueError(f"Unsupported command '{cmd.command}'")
+ except Exception as inner_exc: # noqa: BLE001 - convert to payload
+ response_payload = {"status": "error", "error": str(inner_exc)}
+ await websocket.send_text(
+ cipher.encrypt_json(
+ {
+ "type": "command_response",
+ "command": cmd.command,
+ **response_payload,
+ "timestamp": cmd.timestamp,
+ }
+ )
+ )
+ except (AuthenticationError, HTTPException) as exc:
+ await websocket.close(code=4401, reason=str(exc))
+ except WebSocketDisconnect:
+ pass
+ except Exception as exc:
+ await websocket.close(code=1011, reason=str(exc))
+
+
+async def _heartbeat_sender(websocket: WebSocket, cipher: CipherBox, interval: float) -> None:
+ try:
+ while True:
+ payload = {
+ "type": "heartbeat",
+ "timestamp": time.time()
+ }
+ await websocket.send_text(cipher.encrypt_json(payload))
+ await asyncio.sleep(interval)
+ except asyncio.CancelledError:
+ pass
+ except WebSocketDisconnect:
+ pass
+ except RuntimeError:
+ pass
+
+
+async def _heartbeat_receiver(websocket: WebSocket, cipher: CipherBox) -> None:
+ try:
+ while True:
+ encrypted = await websocket.receive_text()
+ message = cipher.decrypt_json(encrypted)
+ if message.get("type") == "ping":
+ await websocket.send_text(cipher.encrypt_json({"type": "pong", "timestamp": time.time()}))
+ except asyncio.CancelledError:
+ pass
+ except WebSocketDisconnect:
+ pass
+
+
+@app.websocket("/ws/heartbeat")
+async def websocket_heartbeat(websocket: WebSocket) -> None:
+ sender_task = receiver_task = None
+ try:
+ _, cipher = await _perform_handshake(websocket)
+ sender_task = asyncio.create_task(_heartbeat_sender(websocket, cipher, 3.0))
+ receiver_task = asyncio.create_task(_heartbeat_receiver(websocket, cipher))
+ await asyncio.gather(sender_task, receiver_task)
+ except (AuthenticationError, HTTPException) as exc:
+ await websocket.close(code=4401, reason=str(exc))
+ except WebSocketDisconnect:
+ pass
+ except Exception as exc:
+ await websocket.close(code=1011, reason=str(exc))
+ finally:
+ for task in (sender_task, receiver_task):
+ if task:
+ task.cancel()
+ with contextlib.suppress(asyncio.CancelledError):
+ await task
diff --git a/service/broadcast.py b/service/broadcast.py
new file mode 100644
index 000000000..32a63d136
--- /dev/null
+++ b/service/broadcast.py
@@ -0,0 +1,48 @@
+import asyncio
+import threading
+from typing import Any, List, Set, Union
+
+
+class BroadcastChannel:
+ """Simple multi-consumer broadcast based on asyncio queues."""
+
+ def __init__(self, loop: Union[asyncio.AbstractEventLoop, None] = None, max_queue_size: int = 128) -> None:
+ self._loop = loop
+ self._max_queue_size = max_queue_size
+ self._subscribers: Set[asyncio.Queue] = set()
+ self._lock = threading.Lock()
+
+ def set_loop(self, loop: asyncio.AbstractEventLoop) -> None:
+ self._loop = loop
+
+ def subscribe(self) -> asyncio.Queue:
+ queue: asyncio.Queue = asyncio.Queue(maxsize=self._max_queue_size)
+ with self._lock:
+ self._subscribers.add(queue)
+ return queue
+
+ def unsubscribe(self, queue: asyncio.Queue) -> None:
+ with self._lock:
+ self._subscribers.discard(queue)
+
+ async def publish(self, message: Any) -> None:
+ with self._lock:
+ subscribers: List[asyncio.Queue] = list(self._subscribers)
+ for queue in subscribers:
+ try:
+ queue.put_nowait(message)
+ except asyncio.QueueFull:
+ try:
+ queue.get_nowait()
+ except asyncio.QueueEmpty:
+ pass
+ try:
+ queue.put_nowait(message)
+ except asyncio.QueueFull:
+ # Give up if subscriber never consumes
+ pass
+
+ def publish_threadsafe(self, message: Any) -> None:
+ if self._loop is None:
+ raise RuntimeError("Event loop is not set for BroadcastChannel")
+ asyncio.run_coroutine_threadsafe(self.publish(message), self._loop)
diff --git a/service/config_manager.py b/service/config_manager.py
new file mode 100644
index 000000000..3959518d2
--- /dev/null
+++ b/service/config_manager.py
@@ -0,0 +1,479 @@
+from __future__ import annotations
+
+import asyncio
+import json
+import os
+import time
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
+
+from watchfiles import Change, awatch
+
+from .broadcast import BroadcastChannel
+from .diff import PatchConflictError, apply_patch, diff_documents
+from .messages import PatchOperation, SyncPushPayload
+from .utils import read_setup_toml, write_setup_toml
+
+ResourceKey = Tuple[str, Optional[str]]
+
+
+@dataclass
+class ResourceSnapshot:
+ data: Any
+ timestamp: float
+
+
+REPO_URL_CHECK_UPDATE_METHOD_MAPPING = {
+ "https://github.com/pur1fying/blue_archive_auto_script.git": "github",
+ "https://gitee.com/pur1fy/blue_archive_auto_script.git": "gitee",
+ "https://gitcode.com/m0_74686738/blue_archive_auto_script.git": "gitcode",
+ "https://e.coding.net/g-jbio0266/baas/blue_archive_auto_script.git": "tencent_c_coding"
+}
+
+REPO_URL_CHECK_UPDATE_METHOD_MAPPING_REV = {
+ v: k for k, v in REPO_URL_CHECK_UPDATE_METHOD_MAPPING.items()
+}
+
+
+class ConfigManager:
+ """Manages persisted configuration resources with diff support."""
+
+ def __init__(self, project_root: Path, loop: asyncio.AbstractEventLoop | None = None) -> None:
+ self._root = project_root
+ self._config_root = self._root / "config"
+ self._lock = asyncio.Lock()
+ self._snapshots: Dict[ResourceKey, ResourceSnapshot] = {}
+ self._mtimes: Dict[ResourceKey, float] = {}
+ self._update_bus = BroadcastChannel(loop)
+ self._loop = loop
+ self._gui_full: Union[Dict[str, Any], None] = None
+ self._setup_toml: Union[Dict[str, Any], None] = None
+
+ def set_loop(self, loop: asyncio.AbstractEventLoop) -> None:
+ self._loop = loop
+ self._update_bus.set_loop(loop)
+
+ # ------------------------------------------------------------------
+ # basic filesystem helpers
+ # ------------------------------------------------------------------
+ def _file_path(self, resource: str, resource_id: Optional[str]) -> Path:
+ if resource in ("config", "event"):
+ if not resource_id:
+ raise ValueError(f"resource_id required for resource '{resource}'")
+ return self._config_root / resource_id / f"{resource}.json"
+ if resource == "gui":
+ return self._config_root / "gui.json"
+ if resource == "static":
+ return self._config_root / "static.json"
+ if resource == "setup_toml":
+ return self._root / "setup.toml"
+ raise ValueError(f"Unsupported resource '{resource}'")
+
+ def list_config_ids(self) -> List[str]:
+ ids: List[str] = []
+ if not self._config_root.exists():
+ return ids
+ for child in self._config_root.iterdir():
+ if not child.is_dir():
+ continue
+ config_file = child / "config.json"
+ event_file = child / "event.json"
+ if config_file.exists() and event_file.exists():
+ ids.append(child.name)
+ return sorted(ids)
+
+ async def get_config_list(self) -> ResourceSnapshot:
+ async with self._lock:
+ ids = self.list_config_ids()
+ return ResourceSnapshot(json.loads(json.dumps(ids)), time.time())
+
+ # ------------------------------------------------------------------
+ # snapshot helpers
+ # ------------------------------------------------------------------
+ def _project_gui(self, full_gui: Dict[str, Any]) -> Dict[str, Any]:
+ main_window = full_gui.get("MainWindow", {})
+ fluent = full_gui.get("QFluentWidgets", {})
+ return {
+ "MainWindow": {
+ "Language": main_window.get("Language"),
+ "DpiScale": main_window.get("DpiScale"),
+ },
+ "QFluentWidgets": {
+ "ThemeMode": fluent.get("ThemeMode"),
+ },
+ }
+
+ def _project_setup_toml(self, full_setup_toml: Dict[str, Any]) -> Dict[str, Any]:
+ config_URLs = full_setup_toml.get("URLs", {})
+ config_General = full_setup_toml.get("General", {})
+ return {
+ "updateMethod": REPO_URL_CHECK_UPDATE_METHOD_MAPPING.get(config_URLs.get("REPO_URL_HTTP")),
+ "shaMethod": config_General.get("get_remote_sha_method"),
+ "mirrorcCdk": config_General.get("mirrorc_cdk")
+ }
+
+ @staticmethod
+ def gen_event_formation_attr(data: dict) -> Union[dict, None]:
+ result = {}
+ BASE_DIR: str = "src/explore_task_data/activities"
+ for server_mode in ["CN", "JP", "Global"]:
+ current_event = data["current_game_activity"][server_mode]
+ if current_event is None:
+ result[server_mode] = None
+ continue
+
+ file_path = os.path.join(BASE_DIR, f"{current_event}.json")
+
+ if not os.path.exists(file_path):
+ result[server_mode] = None
+ return None
+
+ with open(file_path, "r", encoding="utf-8") as f:
+ stage_data = json.load(f)
+
+ ret = dict(activity_name=current_event, story=dict(), mission=dict(), challenge=dict())
+ en2cn = {
+ "story": "故事",
+ "mission": "任务",
+ "challenge": "挑战",
+ }
+
+ for key, value in stage_data.items():
+ # 直接三大类的列表结构
+ if key in ("story", "mission", "challenge"):
+ for i, v in enumerate(value, start=1):
+ ret[key][f"{en2cn[key]}{i}"] = v
+ continue
+
+ # 其他带编号结构
+ tp = None
+ if key.startswith("story"):
+ tp = "story"
+ elif key.startswith("mission"):
+ tp = "mission"
+ elif key.startswith("challenge"):
+ tp = "challenge"
+ if tp is None:
+ continue
+
+ pos = key.find("_")
+ if pos == -1:
+ continue
+
+ number = key[len(tp):pos]
+ name = f"{en2cn[tp]}{number}"
+ if "sss" in key:
+ name += "三星"
+ if "task" in key:
+ name += "成就任务"
+
+ # 队伍信息
+ team = ""
+ for s in value.get("start", []):
+ temp = s[0]
+ if temp:
+ team += temp + " "
+ if team.endswith(" "):
+ team = team[:-1]
+ ret[tp][name] = team
+ result[server_mode] = ret
+ return result
+
+ def _merge_setup_toml(self, projection: Dict[str, Any]) -> Dict[str, Any]:
+ if self._setup_toml is None:
+ self._setup_toml = {}
+ merged = json.loads(json.dumps(self._setup_toml))
+ merged.setdefault("General", {})
+ merged.setdefault("URLs", {})
+ merged.setdefault("Paths", {})
+
+ shaMethod: Optional[str] = projection.get("shaMethod", None)
+ updateMethod: Optional[str] = projection.get("updateMethod", None)
+ mirrorcCdk: Optional[str] = projection.get("mirrorcCdk", None)
+ if shaMethod is not None:
+ merged["General"]["get_remote_sha_method"] = shaMethod
+ if updateMethod is not None:
+ merged["URLs"]["REPO_URL_HTTP"] = REPO_URL_CHECK_UPDATE_METHOD_MAPPING_REV[updateMethod]
+ if mirrorcCdk is not None:
+ merged["General"]["mirrorc_cdk"] = mirrorcCdk
+
+ self._setup_toml = merged
+ return merged
+
+ def _merge_gui(self, projection: Dict[str, Any]) -> Dict[str, Any]:
+ if self._gui_full is None:
+ self._gui_full = {}
+ merged = json.loads(json.dumps(self._gui_full)) # deep copy via json
+ merged.setdefault("MainWindow", {})
+ merged.setdefault("QFluentWidgets", {})
+ main_window = projection.get("MainWindow", {})
+ if "Language" in main_window:
+ merged["MainWindow"]["Language"] = main_window.get("Language")
+ if "DpiScale" in main_window:
+ merged["MainWindow"]["DpiScale"] = main_window.get("DpiScale")
+ fluent = projection.get("QFluentWidgets", {})
+ if "ThemeMode" in fluent:
+ merged["QFluentWidgets"]["ThemeMode"] = fluent.get("ThemeMode")
+ self._gui_full = merged
+ return merged
+
+ def _load_from_disk(self, resource: str, resource_id: Optional[str]) -> ResourceSnapshot:
+ path = self._file_path(resource, resource_id)
+ data = None
+ if resource == "setup_toml":
+ data, _ = read_setup_toml()
+ else:
+ with path.open("r", encoding="utf-8") as fp:
+ data = json.load(fp)
+ if resource == "gui":
+ self._gui_full = data
+ projection = self._project_gui(data)
+ data_to_store = projection
+ elif resource == "setup_toml":
+ self._setup_toml = data
+ projection = self._project_setup_toml(data)
+ data_to_store = projection
+ else:
+ data_to_store = data
+
+ if resource == "static":
+ event = self.gen_event_formation_attr(data_to_store)
+ data_to_store["current_game_activity"] = event
+
+ timestamp = path.stat().st_mtime
+ snapshot = ResourceSnapshot(data=data_to_store, timestamp=timestamp)
+ self._snapshots[(resource, resource_id)] = snapshot
+ self._mtimes[(resource, resource_id)] = timestamp
+ return snapshot
+
+ async def get_snapshot(self, resource: str, resource_id: Optional[str]) -> ResourceSnapshot:
+ key = (resource, resource_id)
+ async with self._lock:
+ snapshot = self._snapshots.get(key)
+ if snapshot is None:
+ snapshot = self._load_from_disk(resource, resource_id)
+ return ResourceSnapshot(json.loads(json.dumps(snapshot.data)), snapshot.timestamp)
+
+ async def get_static_snapshot(self) -> ResourceSnapshot:
+ snapshot = await self.get_snapshot("static", None)
+
+ return snapshot
+
+ # ------------------------------------------------------------------
+ # update operations
+ # ------------------------------------------------------------------
+ async def apply_patch(
+ self,
+ resource: str,
+ resource_id: Optional[str],
+ operations: Iterable[PatchOperation | dict[str, Any]],
+ timestamp: float,
+ origin: str = "backend",
+ ) -> ResourceSnapshot:
+ key = (resource, resource_id)
+
+ async with self._lock:
+ snapshot = self._snapshots.get(key)
+ if snapshot is None:
+ snapshot = self._load_from_disk(resource, resource_id)
+ elif timestamp < snapshot.timestamp:
+ raise PatchConflictError("Incoming patch is older than current snapshot")
+
+ current_data = json.loads(json.dumps(snapshot.data))
+ ops_payload = [op.model_dump() if isinstance(op, PatchOperation) else dict(op) for op in operations]
+ updated = apply_patch(current_data, ops_payload)
+
+ if resource == "gui":
+ if not isinstance(updated, dict):
+ raise PatchConflictError("GUI patch must result in a mapping document")
+ full_gui = self._merge_gui(updated)
+ elif resource == "setup_toml":
+ if not isinstance(updated, dict):
+ raise PatchConflictError("Setup patch must result in a mapping document")
+ full_setup_toml = self._merge_setup_toml(updated)
+ else:
+ full_gui = updated
+
+ path = self._file_path(resource, resource_id)
+ if resource == "gui":
+ to_dump = full_gui
+ elif resource == "setup_toml":
+ to_dump = full_setup_toml
+ else:
+ to_dump = updated
+
+ if resource == "setup_toml":
+ write_setup_toml(to_dump)
+ else:
+ self._write_json(path, to_dump, resource)
+
+ new_timestamp = max(timestamp, time.time(), path.stat().st_mtime)
+ new_snapshot = ResourceSnapshot(data=updated, timestamp=new_timestamp)
+ self._snapshots[key] = new_snapshot
+ self._mtimes[key] = new_timestamp
+
+ payload = SyncPushPayload(
+ resource=resource, resource_id=resource_id, timestamp=new_snapshot.timestamp, ops=[
+ PatchOperation(**op) for op in ops_payload
+ ], origin=origin,
+ )
+ await self._update_bus.publish(payload.model_dump())
+ return ResourceSnapshot(json.loads(json.dumps(new_snapshot.data)), new_snapshot.timestamp)
+
+ def _write_json(self, path: Path, data: Any, resource: str) -> None:
+ indent = 4 if resource in ("config", "gui") else 2
+ with path.open("w", encoding="utf-8") as fp:
+ json.dump(data, fp, indent=indent, ensure_ascii=False)
+
+ async def subscribe_updates(self) -> asyncio.Queue:
+ if self._loop is None:
+ raise RuntimeError("ConfigManager event loop not configured")
+ return self._update_bus.subscribe()
+
+ def unsubscribe_updates(self, queue: asyncio.Queue) -> None:
+ self._update_bus.unsubscribe(queue)
+
+ # ------------------------------------------------------------------
+ # disk scanning
+ # ------------------------------------------------------------------
+ async def scan_once(self) -> None:
+ current_ids = set(self.list_config_ids())
+ previous_ids = set(self._known_config_ids) if hasattr(self, "_known_config_ids") else set()
+
+ for added in current_ids - previous_ids:
+ await self._notify_config_added(added)
+
+ for removed in previous_ids - current_ids:
+ await self._notify_config_removed(removed)
+
+ self._known_config_ids = current_ids
+
+ for config_id in current_ids:
+ await self._check_resource("config", config_id)
+ await self._check_resource("event", config_id)
+
+ await self._check_resource("gui", None)
+
+ async def _notify_config_added(self, config_id: str):
+ payload = SyncPushPayload(
+ resource="config",
+ resource_id=config_id,
+ timestamp=time.time(),
+ ops=[PatchOperation(op="add", path="", value={})],
+ origin="filesystem",
+ )
+ await self._update_bus.publish(payload.model_dump())
+
+ async def _notify_config_removed(self, config_id: str):
+ payload = SyncPushPayload(
+ resource="config",
+ resource_id=config_id,
+ timestamp=time.time(),
+ ops=[PatchOperation(op="remove", path="", value=None)],
+ origin="filesystem",
+ )
+ # 清理缓存
+ self._mtimes.pop(("config", config_id), None)
+ self._mtimes.pop(("event", config_id), None)
+ self._snapshots.pop(("config", config_id), None)
+ self._snapshots.pop(("event", config_id), None)
+ await self._update_bus.publish(payload.model_dump())
+
+ async def _check_resource(self, resource: str, resource_id: Optional[str]) -> None:
+ key = (resource, resource_id)
+ path = self._file_path(resource, resource_id)
+ if not path.exists():
+ return
+ mtime = path.stat().st_mtime
+ previous = self._mtimes.get(key)
+ if previous is None:
+ self._mtimes[key] = mtime
+ return
+ if mtime <= previous:
+ return
+ async with self._lock:
+ old_snapshot = self._snapshots.get(key)
+ fresh_snapshot = self._load_from_disk(resource, resource_id)
+ if old_snapshot is None:
+ ops = [{"op": "replace", "path": "", "value": fresh_snapshot.data}]
+ else:
+ ops = diff_documents(old_snapshot.data, fresh_snapshot.data)
+ if not ops:
+ return
+ payload = SyncPushPayload(
+ resource=resource,
+ resource_id=resource_id,
+ timestamp=fresh_snapshot.timestamp,
+ ops=[PatchOperation(**op) for op in ops],
+ origin="filesystem",
+ )
+ await self._update_bus.publish(payload.model_dump())
+
+ async def watch_filesystem(self) -> None:
+ if self._loop is None:
+ self._loop = asyncio.get_running_loop()
+ self._update_bus.set_loop(self._loop)
+
+ await self.scan_once()
+
+ try:
+ async for changes in awatch(self._root, recursive=True):
+ await self._handle_watch_batch(changes)
+ except asyncio.CancelledError:
+ raise
+
+ async def _handle_watch_batch(self, changes: Iterable[tuple[Change, str]]) -> None:
+ resource_changes: set[ResourceKey] = set()
+ rescan_configs = False
+ for change, raw_path in changes:
+ path = Path(raw_path)
+ if path.name == "setup.toml" and path.parent == self._root:
+ resource_changes.add(("setup_toml", None))
+ continue
+ if path == self._config_root or self._config_root in path.parents:
+ needs_rescan, resource_key = self._classify_config_change(change, path)
+ rescan_configs = rescan_configs or needs_rescan
+ if resource_key is not None:
+ resource_changes.add(resource_key)
+ if rescan_configs:
+ await self.scan_once()
+ for resource, resource_id in resource_changes:
+ await self._check_resource(resource, resource_id)
+
+ def _classify_config_change(
+ self,
+ change: Change,
+ path: Path,
+ ) -> tuple[bool, Optional[ResourceKey]]:
+ try:
+ relative_parts = path.relative_to(self._config_root).parts
+ except ValueError:
+ return False, None
+
+ if not relative_parts:
+ return True, None
+
+ if len(relative_parts) == 1:
+ name = relative_parts[0]
+ if name == "gui.json":
+ return False, ("gui", None)
+ if name == "static.json":
+ return False, ("static", None)
+ if change != Change.modified:
+ return True, None
+ return False, None
+
+ if len(relative_parts) == 2:
+ config_id, filename = relative_parts
+ if filename == "config.json":
+ return change != Change.modified, ("config", config_id)
+ if filename == "event.json":
+ return change != Change.modified, ("event", config_id)
+ if change != Change.modified:
+ return True, None
+ return False, None
+
+ if change != Change.modified:
+ return True, None
+ return False, None
diff --git a/service/context.py b/service/context.py
new file mode 100644
index 000000000..5c0526306
--- /dev/null
+++ b/service/context.py
@@ -0,0 +1,51 @@
+from __future__ import annotations
+
+import asyncio
+import threading
+from contextlib import suppress
+from pathlib import Path
+from typing import Optional
+
+from .config_manager import ConfigManager
+from .logging import LogManager
+from .runtime import ServiceRuntime
+
+
+class ServiceContext:
+ """Aggregates long-lived service components."""
+
+ def __init__(self, project_root: Path) -> None:
+ self.project_root = project_root
+ self.config_manager = ConfigManager(project_root)
+ self.runtime = ServiceRuntime(project_root)
+ self.log_manager = LogManager()
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
+ self._fs_task: Optional[asyncio.Task] = None
+
+ async def startup(self) -> None:
+ loop = asyncio.get_running_loop()
+ self._loop = loop
+ self.config_manager.set_loop(loop)
+ self.runtime.set_loop(loop)
+ self.log_manager.set_loop(loop)
+
+ await self.runtime.ensure_ready() # ensures OCR server is ready
+ main_queue = self.runtime.get_main_log_queue()
+ self.log_manager.register_queue(main_queue, scope="global")
+ await self.log_manager.start()
+
+ self._fs_task = asyncio.create_task(self.config_manager.watch_filesystem(), name="config-fs-watch")
+ threading.Thread(target=self.runtime.init_all_data).start()
+
+
+ async def shutdown(self) -> None:
+ if self._fs_task:
+ self._fs_task.cancel()
+ with suppress(asyncio.CancelledError):
+ await self._fs_task
+ self._fs_task = None
+ await self.log_manager.stop()
+
+ def ensure_runtime_logger_attached(self) -> None:
+ for queue, scope in self.runtime.get_log_sources():
+ self.log_manager.register_queue(queue, scope=scope)
diff --git a/service/diff.py b/service/diff.py
new file mode 100644
index 000000000..c270ae716
--- /dev/null
+++ b/service/diff.py
@@ -0,0 +1,136 @@
+from __future__ import annotations
+
+import copy
+from typing import Any, Iterable, List
+
+
+class PatchConflictError(Exception):
+ """Raised when an incoming patch cannot be applied cleanly."""
+
+
+def _escape_segment(segment: str) -> str:
+ return segment.replace("~", "~0").replace("/", "~1")
+
+
+def _unescape_segment(segment: str) -> str:
+ return segment.replace("~1", "/").replace("~0", "~")
+
+
+def _split_path(path: str) -> List[str]:
+ if path in ("", "/"):
+ return []
+ if not path.startswith("/"):
+ raise PatchConflictError(f"Invalid json-pointer path: '{path}'")
+ raw_segments = path.split("/")[1:]
+ return [_unescape_segment(seg) for seg in raw_segments]
+
+
+def _resolve_parent(document: Any, segments: Iterable[str]) -> tuple[Any, str]:
+ segments = list(segments)
+ if not segments:
+ raise PatchConflictError("Cannot resolve empty path segments")
+ target = document
+ for seg in segments[:-1]:
+ if isinstance(target, dict):
+ if seg not in target:
+ raise PatchConflictError(f"Missing key '{seg}' while resolving path")
+ target = target[seg]
+ elif isinstance(target, list):
+ try:
+ index = int(seg)
+ except ValueError as exc:
+ raise PatchConflictError(f"Invalid list index '{seg}'") from exc
+ if index < 0 or index >= len(target):
+ raise PatchConflictError(f"List index {index} out of range")
+ target = target[index]
+ else:
+ raise PatchConflictError(f"Cannot traverse into non-container type at segment '{seg}'")
+ return target, segments[-1]
+
+
+def apply_patch(document: Any, operations: Iterable[dict[str, Any]]) -> Any:
+ current = document
+ for op in operations:
+ operation = op.get("op")
+ path = op.get("path", "")
+ value = op.get("value")
+ segments = _split_path(path)
+
+ if not segments:
+ if operation == "replace" or operation == "add":
+ current = copy.deepcopy(value)
+ elif operation == "remove":
+ raise PatchConflictError("Cannot remove the document root")
+ else:
+ raise PatchConflictError(f"Unsupported operation '{operation}'")
+ continue
+
+ parent, last_seg = _resolve_parent(current, segments)
+
+ if isinstance(parent, dict):
+ if operation == "add" or operation == "replace":
+ parent[last_seg] = copy.deepcopy(value)
+ elif operation == "remove":
+ if last_seg not in parent:
+ raise PatchConflictError(f"Key '{last_seg}' not found for removal")
+ del parent[last_seg]
+ else:
+ raise PatchConflictError(f"Unsupported operation '{operation}'")
+ elif isinstance(parent, list):
+ if last_seg == "-":
+ index = len(parent)
+ else:
+ try:
+ index = int(last_seg)
+ except ValueError as exc:
+ raise PatchConflictError(f"Invalid list index '{last_seg}'") from exc
+ if operation == "add":
+ if index < 0 or index > len(parent):
+ raise PatchConflictError(f"List index {index} out of range for add")
+ parent.insert(index, copy.deepcopy(value))
+ elif operation == "replace":
+ if index < 0 or index >= len(parent):
+ raise PatchConflictError(f"List index {index} out of range for replace")
+ parent[index] = copy.deepcopy(value)
+ elif operation == "remove":
+ if index < 0 or index >= len(parent):
+ raise PatchConflictError(f"List index {index} out of range for remove")
+ parent.pop(index)
+ else:
+ raise PatchConflictError(f"Unsupported operation '{operation}'")
+ else:
+ raise PatchConflictError("Target container must be dict or list")
+
+ return current
+
+
+def _diff_dict(old: dict[str, Any], new: dict[str, Any], base_path: str) -> List[dict[str, Any]]:
+ ops: List[dict[str, Any]] = []
+ old_keys = set(old.keys())
+ new_keys = set(new.keys())
+
+ for removed in old_keys - new_keys:
+ path = f"{base_path}/{_escape_segment(removed)}" if base_path else f"/{_escape_segment(removed)}"
+ ops.append({"op": "remove", "path": path})
+
+ for added in new_keys - old_keys:
+ path = f"{base_path}/{_escape_segment(added)}" if base_path else f"/{_escape_segment(added)}"
+ ops.append({"op": "add", "path": path, "value": copy.deepcopy(new[added])})
+
+ for common in old_keys & new_keys:
+ path = f"{base_path}/{_escape_segment(common)}" if base_path else f"/{_escape_segment(common)}"
+ ops.extend(diff_documents(old[common], new[common], path))
+
+ return ops
+
+
+def diff_documents(old: Any, new: Any, base_path: str = "") -> List[dict[str, Any]]:
+ if isinstance(old, dict) and isinstance(new, dict):
+ return _diff_dict(old, new, base_path)
+ if isinstance(old, list) and isinstance(new, list):
+ if old == new:
+ return []
+ return [{"op": "replace", "path": base_path or "", "value": copy.deepcopy(new)}]
+ if old != new:
+ return [{"op": "replace", "path": base_path or "", "value": copy.deepcopy(new)}]
+ return []
diff --git a/service/encryption.py b/service/encryption.py
new file mode 100644
index 000000000..91d4b261c
--- /dev/null
+++ b/service/encryption.py
@@ -0,0 +1,71 @@
+from __future__ import annotations
+
+import base64
+import hashlib
+import hmac
+import json
+import secrets
+from dataclasses import dataclass
+from typing import Any, Union
+
+from cryptography.fernet import Fernet, InvalidToken
+
+
+class AuthenticationError(Exception):
+ """Raised when the websocket handshake fails."""
+
+
+@dataclass
+class HandshakeChallenge:
+ challenge: str
+ algorithm: str = "HMAC-SHA256"
+
+@dataclass
+class HandshakeResponse:
+ response: str
+
+class HandshakeSession:
+ """One-time handshake helper coordinating challenge/response."""
+
+ def __init__(self, shared_secret: str) -> None:
+ if not shared_secret:
+ raise AuthenticationError("Shared secret is not configured")
+ self._shared_secret = shared_secret.encode("utf-8")
+ self._challenge_bytes: Union[bytes, None] = None
+
+ def issue_challenge(self) -> HandshakeChallenge:
+ self._challenge_bytes = secrets.token_bytes(32)
+ challenge = base64.urlsafe_b64encode(self._challenge_bytes).decode("ascii")
+ return HandshakeChallenge(challenge=challenge)
+
+ def verify(self, response: str) -> None:
+ if self._challenge_bytes is None:
+ raise AuthenticationError("Handshake challenge was not issued")
+ expected = hmac.new(self._shared_secret, self._challenge_bytes, hashlib.sha256).hexdigest()
+ if not hmac.compare_digest(expected, response):
+ raise AuthenticationError("Invalid handshake response")
+
+ def build_cipher(self) -> "CipherBox":
+ return CipherBox(self._shared_secret.decode("utf-8"))
+
+
+class CipherBox:
+ """Symmetric encryption box backed by Fernet."""
+
+ def __init__(self, shared_secret: str) -> None:
+ digest = hashlib.sha256(shared_secret.encode("utf-8")).digest()
+ key = base64.urlsafe_b64encode(digest)
+ self._fernet = Fernet(key)
+
+ def encrypt_json(self, payload: dict[str, Any]) -> str:
+ data = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
+ return self._fernet.encrypt(data).decode("utf-8")
+
+ def decrypt_json(self, token: str) -> dict[str, Any]:
+ try:
+ data = self._fernet.decrypt(token.encode("utf-8"))
+
+ except InvalidToken as exc:
+ print("INVALID")
+ raise AuthenticationError("Encrypted payload could not be authenticated") from exc
+ return json.loads(data.decode("utf-8"))
diff --git a/service/logging.py b/service/logging.py
new file mode 100644
index 000000000..8ad0be536
--- /dev/null
+++ b/service/logging.py
@@ -0,0 +1,144 @@
+from __future__ import annotations
+
+import asyncio
+import queue
+import threading
+from collections import defaultdict
+from datetime import datetime, timezone
+from typing import Any, Dict, List, Optional, Set
+
+from .broadcast import BroadcastChannel
+
+LEVEL_MAP = {
+ 1: "INFO",
+ 2: "WARNING",
+ 3: "ERROR",
+ 4: "CRITICAL",
+}
+
+
+class LogManager:
+ """Consumes logger queues and republishes entries tagged by scope."""
+
+ def __init__(self, loop: asyncio.AbstractEventLoop | None = None) -> None:
+ self._loop = loop
+ self._broadcast = BroadcastChannel(loop)
+ self._history_lock = threading.Lock()
+ self._history_all: List[Dict[str, Any]] = []
+ self._history_per_scope: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
+ self._sources: Dict[queue.Queue, str] = {}
+ self._queue_tasks: Dict[queue.Queue, asyncio.Task] = {}
+ self._sentinels: Dict[queue.Queue, object] = {}
+ self._active_tasks: Set[asyncio.Task] = set()
+ self._running = False
+
+ def set_loop(self, loop: asyncio.AbstractEventLoop) -> None:
+ self._loop = loop
+ self._broadcast.set_loop(loop)
+
+ def register_queue(self, log_queue: queue.Queue, scope: str = "global") -> None:
+ if log_queue in self._sources:
+ return
+ self._sources[log_queue] = scope
+ sentinel = object()
+ self._sentinels[log_queue] = sentinel
+ if self._running and self._loop is not None:
+ task = self._loop.create_task(self._pump_async(log_queue, scope, sentinel), name=f"log-pump-{scope}")
+ self._queue_tasks[log_queue] = task
+ self._active_tasks.add(task)
+ task.add_done_callback(self._make_cleanup(log_queue))
+
+ def unregister_queue(self, log_queue: queue.Queue) -> None:
+ scope = self._sources.pop(log_queue, None)
+ if scope is None:
+ return
+ task = self._queue_tasks.pop(log_queue, None)
+ sentinel = self._sentinels.pop(log_queue, None)
+ if task is not None and sentinel is not None:
+ try:
+ log_queue.put_nowait(sentinel)
+ except queue.Full:
+ # Queues provided by the logger are unbounded; ignore if full.
+ pass
+
+ async def start(self) -> None:
+ if self._running:
+ return
+ if self._loop is None:
+ raise RuntimeError("LogManager loop is not configured")
+ self._running = True
+ for idx, (src, scope) in enumerate(self._sources.items()):
+ sentinel = self._sentinels.setdefault(src, object())
+ task = self._loop.create_task(
+ self._pump_async(src, scope, sentinel),
+ name=f"log-pump-{idx}",
+ )
+ self._queue_tasks[src] = task
+ self._active_tasks.add(task)
+ task.add_done_callback(self._make_cleanup(src))
+
+ async def stop(self) -> None:
+ if not self._running:
+ return
+ self._running = False
+ tasks = list(self._active_tasks)
+ for queue_obj, sentinel in list(self._sentinels.items()):
+ try:
+ queue_obj.put_nowait(sentinel)
+ except queue.Full:
+ # Queues provided by the logger are unbounded; ignore if full.
+ pass
+ if tasks:
+ await asyncio.gather(*tasks, return_exceptions=True)
+ self._queue_tasks.clear()
+ self._active_tasks.clear()
+ self._sentinels.clear()
+
+ def _make_cleanup(self, queue_obj: queue.Queue):
+ def _cleanup(task: asyncio.Task) -> None:
+ self._active_tasks.discard(task)
+ self._queue_tasks.pop(queue_obj, None)
+ self._sentinels.pop(queue_obj, None)
+
+ return _cleanup
+
+ async def _pump_async(self, source: queue.Queue, scope: str, sentinel: object) -> None:
+ while True:
+ record = await asyncio.to_thread(source.get)
+ if record is sentinel:
+ break
+ entry = self._normalize_record(record, scope)
+ with self._history_lock:
+ self._history_all.append(entry)
+ self._history_per_scope[scope].append(entry)
+ await self._broadcast.publish(entry)
+
+ def _normalize_record(self, record: Dict[str, Any], scope: str) -> Dict[str, Any]:
+ timestamp = record.get("time")
+ if isinstance(timestamp, datetime):
+ iso = timestamp.astimezone(timezone.utc).isoformat()
+ else:
+ iso = datetime.now(timezone.utc).isoformat()
+ level = LEVEL_MAP.get(record.get("level", 1), "INFO")
+ message = record.get("message", "")
+ return {"scope": scope, "time": iso, "level": level, "message": message}
+
+ def get_history(self, scope: Optional[str] = None) -> List[Dict[str, Any]]:
+ with self._history_lock:
+ if scope is None:
+ return list(self._history_all)
+ return list(self._history_per_scope.get(scope, []))
+
+ def get_scopes(self) -> List[str]:
+ with self._history_lock:
+ scopes = set(self._history_per_scope.keys())
+ scopes.update(self._sources.values())
+ return sorted(scopes)
+
+ async def subscribe(self) -> asyncio.Queue:
+ if self._loop is None:
+ raise RuntimeError("LogManager loop is not configured")
+ return self._broadcast.subscribe()
+
+ def unsubscribe(self, queue_obj: asyncio.Queue) -> None:
+ self._broadcast.unsubscribe(queue_obj)
diff --git a/service/messages.py b/service/messages.py
new file mode 100644
index 000000000..7ad211bb2
--- /dev/null
+++ b/service/messages.py
@@ -0,0 +1,64 @@
+from __future__ import annotations
+
+from typing import Any, Dict, List, Literal, Optional, Union
+
+from pydantic import AliasChoices, BaseModel, Field, field_validator
+
+
+class PatchOperation(BaseModel):
+ op: Literal["add", "remove", "replace"]
+ path: str
+ value: Union[Any, None] = None
+
+ @field_validator("path")
+ def validate_path(cls, value: str) -> str:
+ if value != "" and not value.startswith("/"):
+ raise ValueError("json-pointer paths must start with '/' or be empty")
+ return value
+
+
+class SyncPullMessage(BaseModel):
+ type: Literal["pull"]
+ resource: Literal["config", "event", "gui", "static", "setup_toml"]
+ resource_id: Optional[str] = None
+ is_include_diff_baseline: bool = Field(
+ default=False,
+ description="Whether to return baseline timestamp",
+ validation_alias=AliasChoices("is_include_diff_baseline", "include_diff_baseline"),
+ )
+
+
+class SyncPatchMessage(BaseModel):
+ type: Literal["patch"]
+ resource: Literal["config", "event", "gui", "setup_toml"]
+ resource_id: Optional[str] = None
+ timestamp: float
+ ops: List[PatchOperation]
+
+
+class ProviderRequest(BaseModel):
+ type: Literal["static_request", "status_request"]
+ resource_id: Optional[str] = None
+
+
+class CommandMessage(BaseModel):
+ type: Literal["command"]
+ command: str
+ timestamp: float
+ config_id: Optional[str] = None
+ payload: Dict[str, Any] = Field(default_factory=dict)
+
+
+class HandshakeResponse(BaseModel):
+ type: Literal["handshake_response"]
+ response: str
+ timestamp: float
+
+
+class SyncPushPayload(BaseModel):
+ type: Literal["patch"] = "patch"
+ resource: Literal["config", "event", "gui", "setup_toml"]
+ resource_id: Optional[str] = None
+ timestamp: float
+ ops: List[PatchOperation]
+ origin: Literal["backend", "filesystem", "frontend"] = "backend"
diff --git a/service/runtime.py b/service/runtime.py
new file mode 100644
index 000000000..a4e09493a
--- /dev/null
+++ b/service/runtime.py
@@ -0,0 +1,342 @@
+from __future__ import annotations
+
+import os
+import asyncio
+import datetime
+import shutil
+import threading
+import time
+from dataclasses import dataclass, field
+from pathlib import Path
+from typing import Any, Dict, List, Optional, Tuple
+
+from core.Baas_thread import Baas_thread
+from core.config.config_set import ConfigSet
+from core.device import emulator_manager
+from main import Main
+from .broadcast import BroadcastChannel
+from .utils import *
+
+_TASK_ALIAS = {
+ "start_hard_task": "explore_hard_task",
+ "start_normal_task": "explore_normal_task",
+ "start_fhx": "de_clothes",
+ "start_main_story": "main_story",
+ "start_group_story": "group_story",
+ "start_mini_story": "mini_story",
+ "start_explore_activity_story": "explore_activity_story",
+ "start_explore_activity_mission": "explore_activity_mission",
+ "start_explore_activity_challenge": "explore_activity_challenge",
+}
+
+
+class _SignalHook:
+ def __init__(self, callback) -> None:
+ self._callback = callback
+
+ def emit(self, payload): # noqa: ANN001 - Qt compatible signature
+ self._callback(payload)
+
+
+def _default_status(config_id: str) -> Dict[str, Any]:
+ return {
+ "config_id": config_id,
+ "running": False,
+ "is_flag_run": False,
+ "button": None,
+ "current_task": None,
+ "waiting_tasks": [],
+ "exit_code": None,
+ "timestamp": time.time(),
+ }
+
+
+@dataclass
+class ConfigSession:
+ config_id: str
+ config_set: ConfigSet
+ baas: Baas_thread
+ button_signal: _SignalHook
+ update_signal: _SignalHook
+ exit_signal: _SignalHook
+ thread: Optional[threading.Thread] = None
+ status: Dict[str, Any] = field(default_factory=dict)
+
+
+class ServiceRuntime:
+ """Coordinates Baas_thread lifecycle and exposes async-friendly APIs."""
+
+ def __init__(self, project_root: Path, loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
+ self._project_root = project_root
+ self._loop = loop
+ self._status_bus = BroadcastChannel(loop)
+ self._status_lock = threading.Lock()
+ self._sessions: Dict[str, ConfigSession] = {}
+ self._statuses: Dict[str, Dict[str, Any]] = {}
+ self._lock = asyncio.Lock()
+ self._main: Optional[Main] = None
+ self._baas: Optional[Baas_thread] = None
+ self._baas_thread: Optional[threading.Thread] = None
+ self._active_config_id: Optional[str] = None
+ self._update_signal: Optional[_SignalHook] = None
+ self._exit_signal: Optional[_SignalHook] = None
+ self.is_all_data_initialized: bool = False
+
+ def set_loop(self, loop: asyncio.AbstractEventLoop) -> None:
+ self._loop = loop
+ self._status_bus.set_loop(loop)
+
+ # ------------------------------------------------------------------
+ # public utility accessors
+ # ------------------------------------------------------------------
+ def get_main_log_queue(self):
+ self._ensure_main_sync()
+ assert self._main is not None
+ return self._main.logger.log_collector
+
+ def init_all_data(self):
+ assert self._main is not None
+ self._ensure_config()
+ status = self._main.init_all_data()
+ if status:
+ self._status_bus.publish_threadsafe({
+ "is_all_data_initialized": True
+ })
+ self.is_all_data_initialized = True
+
+ def get_log_sources(self) -> List[Tuple[Any, str]]:
+ sources: List[Tuple[Any, str]] = []
+ for session in self._sessions.values():
+ sources.append((session.baas.logger.log_collector, f"config:{session.config_id}"))
+ return sources
+
+ def current_status(self) -> Dict[str, Dict[str, Any]]:
+ with self._status_lock:
+ return {cid: dict(status) for cid, status in self._statuses.items()}
+
+ def list_sessions(self) -> List[str]:
+ return list(self._sessions.keys())
+
+ async def subscribe_status(self) -> asyncio.Queue:
+ if self._loop is None:
+ raise RuntimeError("ServiceRuntime loop is not configured")
+ return self._status_bus.subscribe()
+
+ def unsubscribe_status(self, queue_obj: asyncio.Queue) -> None:
+ self._status_bus.unsubscribe(queue_obj)
+
+ # ------------------------------------------------------------------
+ # lifecycle helpers
+ # ------------------------------------------------------------------
+ def _ensure_main_sync(self) -> None:
+ if self._main is not None:
+ return
+ self._main = Main(ocr_needed=["en-us", "zh-cn"], jsonify=True, lazy_data=True)
+
+ async def _ensure_main(self) -> None:
+ await asyncio.get_running_loop().run_in_executor(None, self._ensure_main_sync)
+
+ @staticmethod
+ def _ensure_config() -> None:
+ config_dir_list = []
+ for _dir_ in os.listdir('./config'):
+ if os.path.isdir(f'./config/{_dir_}'):
+ files = os.listdir(f'./config/{_dir_}')
+ if 'config.json' in files:
+ check_config(_dir_)
+ if len(config_dir_list) == 0:
+ check_config('default_config')
+
+
+ async def ensure_ready(self) -> None:
+ await self._ensure_main()
+
+ def _get_or_create_session(self, config_id: str) -> ConfigSession:
+ session = self._sessions.get(config_id)
+ if session is not None:
+ return session
+ self._ensure_main_sync()
+ assert self._main is not None
+ config = ConfigSet(config_dir=config_id)
+ button = _SignalHook(lambda payload: self._handle_button_signal(config_id, payload))
+ update = _SignalHook(lambda payload: self._handle_update_signal(config_id, payload))
+ exit_signal = _SignalHook(lambda payload: self._handle_exit_signal(config_id, payload))
+ baas = Baas_thread(
+ config,
+ None,
+ button,
+ update,
+ exit_signal,
+ jsonify=True,
+ )
+ baas.set_ocr(self._main.ocr)
+ status = _default_status(config_id)
+ session = ConfigSession(
+ config_id=config_id,
+ config_set=config,
+ baas=baas,
+ button_signal=button,
+ update_signal=update,
+ exit_signal=exit_signal,
+ status=status,
+ )
+ self._sessions[config_id] = session
+ with self._status_lock:
+ self._statuses[config_id] = status
+ self._publish_status(config_id)
+ return session
+
+ async def start_scheduler(self, config_id: str, set_log=None) -> Dict[str, Any]:
+ loop = asyncio.get_running_loop()
+ async with self._lock:
+ session = self._get_or_create_session(config_id)
+ if set_log: set_log()
+ if session.thread and session.thread.is_alive():
+ return {"status": "already-running", "config_id": config_id}
+ init_ok = await loop.run_in_executor(None, session.baas.init_all_data)
+ if not init_ok:
+ raise RuntimeError("Baas_thread initialization failed")
+
+ def runner() -> None:
+ try:
+ session.baas.send("start")
+ finally:
+ session.thread = None
+ self._update_status(config_id, running=False, is_flag_run=session.baas.flag_run, current_task=None,
+ waiting_tasks=[])
+
+ thread = threading.Thread(
+ target=runner,
+ name=f"baas-scheduler-{config_id}",
+ daemon=True,
+ )
+ session.thread = thread
+ thread.start()
+ self._update_status(config_id, running=True, is_flag_run=True, exit_code=None, current_task=None,
+ waiting_tasks=[])
+ return {"status": "started", "config_id": config_id}
+
+ async def stop_scheduler(self, config_id: str) -> Dict[str, Any]:
+ async with self._lock:
+ session = self._sessions.get(config_id)
+ if session is None:
+ return {"status": "unknown-config", "config_id": config_id}
+ if not session.thread:
+ self._update_status(config_id, running=False, is_flag_run=False, current_task=None, waiting_tasks=[])
+ return {"status": "stopped", "config_id": config_id}
+ session.baas.send("stop")
+ thread = session.thread
+ session.thread = None
+ if thread and thread.is_alive():
+ thread.join(timeout=10.0)
+ self._update_status(config_id, running=False, is_flag_run=False, current_task=None, waiting_tasks=[])
+ return {"status": "stopped", "config_id": config_id}
+
+ async def solve_task(self, config_id: str, task_name: str, set_log=None) -> Dict[str, Any]:
+ if task_name in _TASK_ALIAS:
+ task_name = _TASK_ALIAS[task_name]
+ loop = asyncio.get_running_loop()
+ async with self._lock:
+ session = self._sessions.get(config_id)
+ if session is None:
+ session = self._get_or_create_session(config_id)
+ if set_log: set_log()
+ baas = session.baas
+ needs_init = session.baas.scheduler is None
+
+ if needs_init:
+ await loop.run_in_executor(None, baas.init_all_data)
+
+ def _call() -> None:
+ try:
+ self._update_status(config_id, running=True, is_flag_run=True, exit_code=None, current_task=task_name,
+ waiting_tasks=[])
+ baas.flag_run = True
+ baas.send("solve", task_name)
+ finally:
+ session.thread = None
+ self._update_status(config_id, running=False, is_flag_run=session.baas.flag_run, current_task=None,
+ waiting_tasks=[])
+
+ thread = threading.Thread(
+ target=_call,
+ name=f"baas-solver-{task_name}-{config_id}",
+ daemon=True,
+ )
+ session.thread = thread
+ thread.start()
+
+ return {"status": "ok", "task": task_name, "result": 0}
+
+ @staticmethod
+ async def detect_adb() -> List[str]:
+ loop = asyncio.get_running_loop()
+ adb_res = await loop.run_in_executor(None, emulator_manager.autosearch)
+ return adb_res
+
+ @staticmethod
+ async def valid_cdk(cdk):
+ loop = asyncio.get_running_loop()
+ cdk_res = await loop.run_in_executor(None, validate_cdk, cdk)
+ return cdk_res
+
+ @staticmethod
+ async def test_all_sha():
+ loop = asyncio.get_running_loop()
+ all_sha_res = await loop.run_in_executor(None, test_all_repo_sha)
+ return all_sha_res
+
+ @staticmethod
+ async def check_for_update():
+ loop = asyncio.get_running_loop()
+ all_update_res = await loop.run_in_executor(None, check_for_update)
+ return all_update_res
+
+ @staticmethod
+ async def add_config(name, server):
+ serial_name = str(int(datetime.datetime.now().timestamp()))
+ check_config(serial_name, name=name, server=server)
+ return {
+ "serial": serial_name
+ }
+
+ @staticmethod
+ async def remove_config(_id):
+ shutil.rmtree(f'./config/{_id}')
+ return {}
+
+ # ------------------------------------------------------------------
+ # signal handlers
+ # ------------------------------------------------------------------
+ def _handle_button_signal(self, config_id: str, payload: Any) -> None:
+ self._update_status(config_id, button=payload)
+
+ def _handle_update_signal(self, config_id: str, payload: Any) -> None:
+ if isinstance(payload, list) and payload:
+ current = payload[0]
+ waiting = [item for item in payload if item != current]
+ else:
+ current = None
+ waiting = []
+ self._update_status(config_id, current_task=current, waiting_tasks=waiting)
+
+ def _handle_exit_signal(self, config_id: str, payload: Any) -> None:
+ self._update_status(config_id, running=False, is_flag_run=False, exit_code=payload)
+
+ def _update_status(self, config_id: str, **changes: Any) -> None:
+ with self._status_lock:
+ status = self._statuses.setdefault(config_id, _default_status(config_id))
+ status.update(changes)
+ status["timestamp"] = time.time()
+ snapshot = dict(status)
+ self._publish_status(config_id, snapshot)
+
+ def _publish_status(self, config_id: str, snapshot: Optional[Dict[str, Any]] = None) -> None:
+ if snapshot is None:
+ with self._status_lock:
+ status = self._statuses.get(config_id)
+ if status is None:
+ return
+ snapshot = dict(status)
+ if self._loop:
+ self._status_bus.publish_threadsafe({"config_id": config_id, "status": snapshot})
diff --git a/service/utils/__init__.py b/service/utils/__init__.py
new file mode 100644
index 000000000..5b073239e
--- /dev/null
+++ b/service/utils/__init__.py
@@ -0,0 +1,12 @@
+from .update import *
+from .config_ops import *
+
+__all__ = [
+ "VersionInfo",
+ "check_for_update",
+ "test_all_repo_sha",
+ "validate_cdk",
+ "read_setup_toml",
+ "write_setup_toml",
+ "check_config"
+]
diff --git a/service/utils/config_ops.py b/service/utils/config_ops.py
new file mode 100644
index 000000000..18b62d076
--- /dev/null
+++ b/service/utils/config_ops.py
@@ -0,0 +1,147 @@
+import datetime
+import json
+import os
+
+from core.config import default_config
+from core.config.config_set import ConfigSet
+
+
+def check_switch_config(dir_path='./default_config'):
+ path = './config/' + dir_path + '/switch.json'
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(default_config.SWITCH_DEFAULT_CONFIG)
+
+
+def check_single_event(new_event, old_event):
+ for key in new_event:
+ if key not in old_event:
+ old_event[key] = new_event[key]
+ return old_event
+
+
+def check_event_config(dir_path='./default_config', user_config=None):
+ path = './config/' + dir_path + '/event.json'
+ default_event_config = json.loads(default_config.EVENT_DEFAULT_CONFIG)
+ server = user_config.server_mode
+ enable_state = user_config.config.new_event_enable_state
+ if server != "CN":
+ for i in range(0, len(default_event_config)):
+ for j in range(0, len(default_event_config[i]['daily_reset'])):
+ default_event_config[i]['daily_reset'][j][0] = default_event_config[i]['daily_reset'][j][0] - 1
+ if not os.path.exists(path):
+ with open(path, 'w', encoding='utf-8') as f:
+ with open("error.log", 'w+', encoding='utf-8') as errorfile:
+ errorfile.write("path not exist" + '\n' + dir_path + '\n' + datetime.datetime.now().strftime(
+ '%Y-%m-%d %H:%M:%S') + '\n')
+ f.write(json.dumps(default_event_config, ensure_ascii=False, indent=2))
+ return
+ try:
+ with open(path, 'r', encoding='utf-8') as f: # 检查是否有新的配置项
+ data = json.load(f)
+ for i in range(0, len(default_event_config)):
+ exist = False
+ for j in range(0, len(data)):
+ if data[j]['func_name'] == default_event_config[i]['func_name']:
+ for k in range(0, len(data[i]['daily_reset'])):
+ if len(data[j]['daily_reset'][k]) != 3:
+ data[j]['daily_reset'][k] = [0, 0, 0]
+ data[j] = check_single_event(default_event_config[i], data[j])
+ exist = True
+ break
+ if not exist:
+ temp = default_event_config[i]
+ if enable_state == "on":
+ temp['enabled'] = True
+ elif enable_state == "off":
+ temp['enabled'] = False
+ data.insert(i, temp)
+
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(json.dumps(data, ensure_ascii=False, indent=2))
+ except Exception as e:
+ with open("error.log", 'w+', encoding='utf-8') as f:
+ f.write(str(e) + '\n' + dir_path + '\n' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + '\n')
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(json.dumps(default_event_config, ensure_ascii=False, indent=2))
+ return
+
+
+def delete_deprecated_config(file_name, config_name=None):
+ # delete useless config file
+ pre = 'config/'
+ if config_name is not None:
+ pre += config_name + '/'
+ if type(file_name) is str:
+ path = pre + file_name
+ if os.path.exists(path):
+ os.remove(path)
+ elif type(file_name) is list:
+ for name in file_name:
+ path = pre + name
+ if os.path.exists(path):
+ os.remove(path)
+
+
+def check_static_config():
+ if not os.path.exists('./config/static.json'):
+ with open('./config/static.json', 'w', encoding='utf-8') as f:
+ f.write(default_config.STATIC_DEFAULT_CONFIG)
+ return
+ try:
+ with open('./config/static.json', 'w', encoding='utf-8') as f:
+ f.write(default_config.STATIC_DEFAULT_CONFIG)
+ except Exception:
+ os.remove('./config/static.json')
+ with open('./config/static.json', 'w', encoding='utf-8') as f:
+ f.write(default_config.STATIC_DEFAULT_CONFIG)
+
+
+def update_config_reserve_old(config_old, config_new): # 保留旧配置原有的键,添加新配置中没有的,删除新配置中没有的键
+ for key in config_new:
+ if key not in config_old:
+ config_old[key] = config_new[key]
+ dels = []
+ for key in config_old:
+ if key not in config_new:
+ dels.append(key)
+ for key in dels:
+ del config_old[key]
+ return config_old
+
+
+def check_and_update_user_config(dir_path='./default_config', server=None, name=None):
+ path = './config/' + dir_path + '/config.json'
+ if not os.path.exists(path):
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(default_config.DEFAULT_CONFIG)
+ try:
+ with open(path, 'r', encoding='utf-8') as f:
+ data = json.load(f)
+ data = update_config_reserve_old(data, json.loads(default_config.DEFAULT_CONFIG))
+ if name is not None:
+ data['name'] = name
+ if server is not None:
+ data['server'] = server
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(json.dumps(data, ensure_ascii=False, indent=2))
+ except Exception:
+ os.remove(path)
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(default_config.DEFAULT_CONFIG)
+
+
+def check_config(dir_path, server=None, name=None):
+ delete_deprecated_config("display.json", dir_path)
+ if not os.path.exists('./config'):
+ os.mkdir('./config')
+ check_static_config()
+ if type(dir_path) is not list:
+ dir_path = [dir_path]
+ for path in dir_path:
+ if not os.path.exists('./config/' + path):
+ os.mkdir('./config/' + path)
+ check_and_update_user_config(path, server, name)
+ config = ConfigSet(config_dir=path)
+ config.update_create_quantity_entry()
+ check_event_config(path, config)
+ check_switch_config(path)
diff --git a/service/utils/update.py b/service/utils/update.py
new file mode 100644
index 000000000..3848fa0dc
--- /dev/null
+++ b/service/utils/update.py
@@ -0,0 +1,300 @@
+from __future__ import annotations
+
+import time
+from dataclasses import dataclass
+from datetime import datetime, timezone
+from pathlib import Path
+from types import SimpleNamespace
+from typing import Any, Dict, List, Optional, Tuple, Union
+
+import pygit2
+import requests
+import tomli_w
+
+try: # Python 3.11+
+ import tomllib # type: ignore[import]
+except ModuleNotFoundError: # pragma: no cover - Python < 3.11 fallback
+ import tomli as tomllib # type: ignore[import]
+
+from deploy.installer.const import GetShaMethod, get_remote_sha_methods
+from deploy.installer.mirrorc_update.const import MirrorCErrorCode
+from deploy.installer.mirrorc_update.mirrorc_updater import MirrorC_Updater
+from deploy.installer.toml_config import DEFAULT_SETTINGS
+
+RepositoryResult = Dict[str, Any]
+
+
+@dataclass
+class VersionInfo:
+ """Normalized version data loaded from setup.toml."""
+
+ version: Optional[str]
+ source: str
+ path: Path
+
+ def to_dict(self) -> Dict[str, Any]:
+ return {
+ "version": self.version,
+ "source": self.source,
+ "path": str(self.path),
+ }
+
+
+def _github_api_get_latest_sha(config: Dict[str, Any], timeout: float) -> Tuple[bool, str]:
+ owner = config["owner"]
+ repo = config["repo"]
+ branch = config["branch"]
+ url = f"https://api.github.com/repos/{owner}/{repo}/branches/{branch}"
+ try:
+ response = requests.get(url, timeout=timeout)
+ response.raise_for_status()
+ response_json = response.json()
+ sha_value = response_json.get("commit", {}).get("sha")
+ if not sha_value:
+ return False, "Commit SHA not found in GitHub response"
+ return True, sha_value
+ except requests.RequestException as exc: # pragma: no cover - network errors
+ return False, str(exc)
+
+
+def _mirrorc_api_get_latest_sha(timeout: float) -> Tuple[bool, str]:
+ mirrorc_inst = MirrorC_Updater(app="BAAS_repo", current_version="")
+ try:
+ ret = mirrorc_inst.get_latest_version(cdk="", timeout=timeout)
+ if ret.has_data and ret.latest_version_name:
+ return True, ret.latest_version_name
+ return False, ret.message
+ except Exception as exc: # pragma: no cover - external dependency
+ return False, str(exc)
+
+
+def _pygit2_get_latest_sha(config: Dict[str, Any]) -> Tuple[bool, str]:
+ url = config["url"]
+ branch = config["branch"]
+ target_ref = f"refs/heads/{branch}"
+ try:
+ git = pygit2.Repository(".")
+ remote = git.remotes.create_anonymous(url)
+ for head in remote.ls_remotes():
+ if head.get("name") == target_ref:
+ return True, str(head.get("oid"))
+ return False, f"Branch '{branch}' not found at {url}"
+ except Exception as exc:
+ return False, str(exc)
+
+
+def test_all_repo_sha(timeout: float = 3.0) -> List[RepositoryResult]:
+ """Test every configured repository and return timing + SHA details."""
+ results: List[RepositoryResult] = []
+ for config in get_remote_sha_methods:
+ result = test_repo_sha(config, timeout)
+ results.append(result)
+ return results
+
+
+def test_repo_sha(config: dict[str, Union[str, GetShaMethod]], timeout: float) -> dict[str, Any]:
+ method = config["method"]
+ start = time.perf_counter()
+ if method == GetShaMethod.GITHUB_API:
+ success, value = _github_api_get_latest_sha(config, timeout)
+ elif method == GetShaMethod.MIRRORC_API:
+ success, value = _mirrorc_api_get_latest_sha(timeout)
+ else:
+ success, value = _pygit2_get_latest_sha(config)
+ elapsed = time.perf_counter() - start
+ result: RepositoryResult = {
+ "name": config.get("name"),
+ "method": method.name,
+ "duration": elapsed,
+ "success": success,
+ "value": value if success else None,
+ "error": None if success else value,
+ }
+ return result
+
+
+def _format_expired_time(timestamp_value: Optional[float]) -> Tuple[Optional[float], Optional[str]]:
+ if not timestamp_value:
+ return None, None
+ dt = datetime.fromtimestamp(timestamp_value, tz=timezone.utc)
+ return timestamp_value, dt.isoformat()
+
+
+def validate_cdk(cdk: str, timeout: float = 3.0) -> Dict[str, Any]:
+ """Validate a MirrorC CDK and return structured status information."""
+ if cdk != "":
+ updater = MirrorC_Updater(app="BAAS_repo", current_version="")
+ try:
+ ret = updater.get_latest_version(cdk=cdk, timeout=timeout)
+ except requests.RequestException as exc:
+ data, path_setup_toml = read_setup_toml()
+ data["General"]["mirrorc_cdk"] = ""
+ write_setup_toml(data, path_setup_toml)
+ return {
+ "success": False,
+ "code": None,
+ "message": str(exc),
+ "latest_version": None,
+ "expires_at": None,
+ "expires_at_iso": None,
+ "mirrorc_message": None,
+ }
+ except Exception as exc: # pragma: no cover - unexpected error
+ data, path_setup_toml = read_setup_toml()
+ data["General"]["mirrorc_cdk"] = ""
+ write_setup_toml(data, path_setup_toml)
+ return {
+ "success": False,
+ "code": None,
+ "message": str(exc),
+ "latest_version": None,
+ "expires_at": None,
+ "expires_at_iso": None,
+ "mirrorc_message": None,
+ }
+ else:
+ ret = SimpleNamespace(
+ code=MirrorCErrorCode.KEY_INVALID.value,
+ message="CDK invalid.",
+ latest_version_name=None
+ )
+
+ code = ret.code
+ expires_ts = getattr(ret, "cdk_expired_time", None)
+ expires_ts, expires_iso = _format_expired_time(expires_ts)
+
+ base_messages = {
+ MirrorCErrorCode.SUCCESS.value: (
+ "CDK valid. Expires at {}" if expires_iso else "CDK valid."
+ ),
+ MirrorCErrorCode.KEY_EXPIRED.value: "CDK expired.",
+ MirrorCErrorCode.KEY_INVALID.value: "CDK invalid.",
+ MirrorCErrorCode.RESOURCE_QUOTA_EXHAUSTED.value: "CDK quota exhausted for today.",
+ MirrorCErrorCode.KEY_MISMATCHED.value: "CDK mismatched for requested resource.",
+ MirrorCErrorCode.KEY_BLOCKED.value: "CDK blocked.",
+ }
+ message = base_messages.get(code, f"MirrorC returned code {code}.")
+
+ data, path_setup_toml = read_setup_toml()
+ data["General"]["mirrorc_cdk"] = cdk if code == MirrorCErrorCode.SUCCESS.value else ""
+ write_setup_toml(data, path_setup_toml)
+
+ return {
+ "success": code == MirrorCErrorCode.SUCCESS.value,
+ "code": code,
+ "message": message,
+ "mirrorc_message": getattr(ret, "message", None),
+ "latest_version": getattr(ret, "latest_version_name", None),
+ "expires_at": expires_ts,
+ "expires_at_iso": expires_iso,
+ }
+
+
+def get_local_version(setup_path: Optional[Path] = None) -> Tuple["VersionInfo", Dict[str, Any], str]:
+ data, path = read_setup_toml(setup_path)
+ general_section = data.get("General", {})
+ version_value = general_section.get("current_BAAS_version")
+ try:
+ repo = pygit2.Repository(Path.cwd())
+ _branch = repo.head.shorthand
+ if not version_value:
+ commit = repo.revparse_single("HEAD")
+ version_value = str(commit.id)
+ data.setdefault("General", {})["current_BAAS_version"] = version_value
+ with path.open("wb") as file:
+ tomli_w.dump(data, file)
+ except Exception:
+ version_value = ""
+ _branch = "master"
+
+ return VersionInfo(version=version_value, source="setup.toml", path=path), data, _branch
+
+
+def read_setup_toml(setup_path: Union[Path, None] = None) -> tuple[dict[str, Any], Union[Path, None]]:
+ path = setup_path or (Path.cwd() / "setup.toml")
+ if not path.exists():
+ with path.open("wb") as file:
+ tomli_w.dump(DEFAULT_SETTINGS, file)
+
+ with path.open("rb") as fp:
+ data = tomllib.load(fp)
+ return data, path
+
+
+def write_setup_toml(content: dict, setup_path: Union[Path, None] = None) -> None:
+ path = setup_path or (Path.cwd() / "setup.toml")
+ if not path.exists():
+ with path.open("wb") as file:
+ tomli_w.dump(DEFAULT_SETTINGS, file)
+
+ with path.open("wb") as fp:
+ tomli_w.dump(content, fp)
+
+
+def _select_remote_record(
+ local_version: Optional[str],
+ results: List[RepositoryResult],
+) -> Optional[RepositoryResult]:
+ for record in results:
+ value = record.get("value")
+ if not record.get("success") or not value:
+ continue
+ if not local_version:
+ return record
+ if len(value) == len(local_version):
+ return record
+ return next((record for record in results if record.get("success") and record.get("value")), None)
+
+
+def check_for_update(timeout: float = 3.0) -> Dict[str, Any]:
+ """
+ Detect whether a newer version is available.
+
+ The function compares the version retrieved from setup.toml with the first
+ successful remote value (preferring matching formats) and returns a
+ structured response that can be consumed by service endpoints.
+ """
+ try:
+ local_info, setup_toml, _branch = get_local_version()
+ method_name = setup_toml.setdefault("General", {}).get("get_remote_sha_method", None)
+ if not method_name:
+ repo_results = test_all_repo_sha(timeout=timeout)
+ method_name = _select_remote_record(local_info.version, repo_results).get("name")
+ setup_toml.setdefault("General", {})["get_remote_sha_method"] = method_name
+ with local_info.path.open("wb") as file:
+ tomli_w.dump(setup_toml, file)
+
+ for ind, x in enumerate(get_remote_sha_methods):
+ if "branch" in x:
+ get_remote_sha_methods[ind]["branch"] = _branch
+
+ method_config = list(filter(lambda x: x.get('name') == method_name, get_remote_sha_methods))[0]
+ repo_result = test_repo_sha(method_config, timeout=timeout)
+ remote_version = repo_result.get("value") if repo_result else None
+
+ update_available = (
+ bool(local_info.version)
+ and bool(remote_version)
+ and local_info.version != remote_version
+ )
+
+ return {
+ "local": local_info.version,
+ "remote": remote_version,
+ "update_available": update_available
+ }
+
+ except:
+ import traceback
+ traceback.print_exc()
+ return {}
+
+
+__all__ = [
+ "check_for_update",
+ "test_all_repo_sha",
+ "validate_cdk",
+ "VersionInfo",
+ "read_setup_toml",
+ "write_setup_toml"
+]
From 888ecb3e23a788f06600104444943443bd84b8a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Fri, 21 Nov 2025 23:38:33 +0800
Subject: [PATCH 02/98] Fix: uv does need setuptools to support adb
---
requirements.service.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/requirements.service.txt b/requirements.service.txt
index 4d90f146c..f568bc4e2 100644
--- a/requirements.service.txt
+++ b/requirements.service.txt
@@ -1,6 +1,7 @@
# =========================== #
# Core Utils #
numpy < 2.0 #
+setuptools #
adbutils == 2.2.1 #
uiautomator2 == 2.16.23 #
opencv-python == 4.8.1.78 #
From a6c14256003578286e4db17bda4dfc40da78621d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Mon, 24 Nov 2025 12:06:05 +0800
Subject: [PATCH 03/98] Update: Check local version every time
---
service/utils/update.py | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/service/utils/update.py b/service/utils/update.py
index 3848fa0dc..bd10b146e 100644
--- a/service/utils/update.py
+++ b/service/utils/update.py
@@ -192,17 +192,14 @@ def validate_cdk(cdk: str, timeout: float = 3.0) -> Dict[str, Any]:
def get_local_version(setup_path: Optional[Path] = None) -> Tuple["VersionInfo", Dict[str, Any], str]:
data, path = read_setup_toml(setup_path)
- general_section = data.get("General", {})
- version_value = general_section.get("current_BAAS_version")
try:
repo = pygit2.Repository(Path.cwd())
_branch = repo.head.shorthand
- if not version_value:
- commit = repo.revparse_single("HEAD")
- version_value = str(commit.id)
- data.setdefault("General", {})["current_BAAS_version"] = version_value
- with path.open("wb") as file:
- tomli_w.dump(data, file)
+ commit = repo.revparse_single("HEAD")
+ version_value = str(commit.id)
+ data.setdefault("General", {})["current_BAAS_version"] = version_value
+ with path.open("wb") as file:
+ tomli_w.dump(data, file)
except Exception:
version_value = ""
_branch = "master"
From 823438614bfbc95fd6705365eb9e87ba30bd22d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Wed, 3 Dec 2025 18:20:26 +0800
Subject: [PATCH 04/98] Update requirements for Sub Utils and rich package
---
requirements.service.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/requirements.service.txt b/requirements.service.txt
index f568bc4e2..974f4e7ed 100644
--- a/requirements.service.txt
+++ b/requirements.service.txt
@@ -8,9 +8,10 @@ opencv-python == 4.8.1.78 #
# =========================== #
# =========================== #
-# Process & Screenshot #
+# Sub Utils #
psutil #
requests #
+rich >= 14.2.0 #
# =========================== #
# =========================== #
From 51269d637bc1a2e56d0f5b20d6eab03f7be47a8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Wed, 3 Dec 2025 20:07:15 +0800
Subject: [PATCH 05/98] Fix: Refactor logging to use rich formatting and
centralize setup
Replaces custom logging and console output with a centralized rich-based logging format. Moves log handler and formatter setup to service/__init__.py via set_log_format(), and updates main.service.py and core/utils.py to use standard logging. Adds logging for config changes in config_manager.py and suppresses specific warnings for cleaner output.
---
core/utils.py | 12 ++------
main.service.py | 33 ++--------------------
service/__init__.py | 59 +++++++++++++++++++++++++++++++++++++--
service/config_manager.py | 23 +++++++++++++++
4 files changed, 85 insertions(+), 42 deletions(-)
diff --git a/core/utils.py b/core/utils.py
index e3c9ff7c9..805625344 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -1,11 +1,11 @@
import logging
import sys
import threading
+import queue
from datetime import datetime, timedelta, timezone
from typing import Union
from rich.console import Console
-from rich.markup import escape
console = Console()
@@ -88,25 +88,19 @@ def __out__(self, message: str, level: int = 1, raw_print=False) -> None:
"level": level,
"message": message
})
- print(
- f'{STATUS_TERMINAL[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}')
+ logging.log((level + 1) * 10, message)
return
while len(logging.root.handlers) > 0:
logging.root.handlers.pop()
- levels_str = ["INFO", "WARNING", "ERROR", "CRITICAL"]
# If logger signal is not None, output log to logger signal
# else output log to console
- levels_color = ["#2d8cf0", "#ff9900", "#ed3f14", "#3e0480"]
if self.logger_signal is not None:
self.logs += f"{levels_str[level - 1]} | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | {message}"
self.logger_signal.emit(level, message)
else:
- console.print(f'[{levels_color[level - 1]}]'
- f'{levels_str[level - 1]} |'
- f' {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} |'
- f' {escape(message)}[/]', soft_wrap=True)
+ logging.log((level + 1) * 10, message)
def info(self, message: str) -> None:
"""
diff --git a/main.service.py b/main.service.py
index 41272bee4..7c0fc14e1 100644
--- a/main.service.py
+++ b/main.service.py
@@ -1,41 +1,11 @@
import argparse
import os
-
import uvicorn
+from service import set_log_format
DEFAULT_HOST = os.getenv("BAAS_SERVICE_HOST", "127.0.0.1")
DEFAULT_PORT = int(os.getenv("BAAS_SERVICE_PORT", "8190"))
-import logging, re
-
-ANSI_ESCAPE = re.compile(r'\x1b\[([0-9;]*[mGKH])')
-
-STATUS_TERMINAL = {
- logging.INFO: " INFO",
- logging.WARNING: " WARNING",
- logging.ERROR: " ERROR",
- logging.CRITICAL: "CRITICAL",
-}
-
-class PlainFormatter(logging.Formatter):
- def format(self, record):
- level = STATUS_TERMINAL.get(record.levelno, " INFO")
- log_fmt = f"{level} | %(asctime)s | %(message)s"
- formatter = logging.Formatter(log_fmt, "%Y-%m-%d %H:%M:%S")
- output = formatter.format(record)
-
- return ANSI_ESCAPE.sub('', output)
-
-
-handler = logging.StreamHandler()
-handler.setFormatter(PlainFormatter())
-
-root = logging.getLogger()
-root.setLevel(logging.INFO)
-root.handlers = [handler]
-
-
-
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Start BAAS service mode backend")
@@ -47,6 +17,7 @@ def parse_args() -> argparse.Namespace:
def main() -> None:
+ set_log_format()
args = parse_args()
config = uvicorn.Config(
"service.app:app",
diff --git a/service/__init__.py b/service/__init__.py
index 5068dd815..e5ac92168 100644
--- a/service/__init__.py
+++ b/service/__init__.py
@@ -1,4 +1,59 @@
-from .app import app, context
+def set_log_format():
+ import logging
+ from datetime import datetime
+ from rich.console import Console
+ from rich.markup import escape
+
+ console = Console()
+
+ levels_label = {
+ logging.INFO: ("[INFO]", "#2d8cf0"),
+ logging.WARNING: ("[WARN]", "#ff9900"),
+ logging.ERROR: ("[ERRO]", "#ed3f14"),
+ logging.CRITICAL: ("[CRIT]", "#7c3aed"),
+ }
+
+ class RichFormatter(logging.Formatter):
+ def format(self, record):
+ label, color = levels_label.get(record.levelno, ("INFO", "cyan"))
+ time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ msg = escape(record.getMessage())
+ level_tag = f"[{color} bold]{label}[/]"
+ time_tag = f"[dim]{time_str}[/dim]"
+ if record.levelno != logging.INFO:
+ msg = f"[{color} bold]{msg}[/]"
+ return f"{time_tag} {level_tag} {msg}"
+
+ # ----------- Handler 直接输出到 console.print -----------
+ class RichHandler(logging.Handler):
+ def emit(self, record):
+ try:
+ msg = self.format(record)
+ console.print(msg, soft_wrap=True)
+ except Exception:
+ self.handleError(record)
+
+ # ----------- 使用 ----------
+ handler = RichHandler()
+ handler.setFormatter(RichFormatter())
-__all__ = ['app', 'context']
+ root = logging.getLogger()
+ root.setLevel(logging.INFO)
+ root.handlers = [handler]
+
+ logging.getLogger("watchfiles").setLevel(logging.ERROR)
+ logging.getLogger("watchfiles.main").setLevel(logging.ERROR)
+
+
+import warnings
+
+# Suppress warning from adbutils
+warnings.filterwarnings(
+ "ignore",
+ message="pkg_resources is deprecated as an API",
+ category=UserWarning
+)
+
+from .app import app, context
+__all__ = ['app', 'context', 'set_log_format']
diff --git a/service/config_manager.py b/service/config_manager.py
index 3959518d2..925984d8e 100644
--- a/service/config_manager.py
+++ b/service/config_manager.py
@@ -2,6 +2,7 @@
import asyncio
import json
+import logging
import os
import time
from dataclasses import dataclass
@@ -424,10 +425,32 @@ async def watch_filesystem(self) -> None:
raise
async def _handle_watch_batch(self, changes: Iterable[tuple[Change, str]]) -> None:
+ watch_dirs = [
+ self._config_root,
+ ]
+
+ watch_files = [
+ self._root / "setup.toml",
+ ]
+
resource_changes: set[ResourceKey] = set()
rescan_configs = False
for change, raw_path in changes:
path = Path(raw_path)
+
+ in_watch_dir = any(path == d or d in path.parents for d in watch_dirs)
+ in_watch_file = any(path == f for f in watch_files)
+
+ if not (in_watch_dir or in_watch_file):
+ continue
+
+ try:
+ rel_path = path.relative_to(self._root)
+ except ValueError:
+ rel_path = path
+
+ logging.info(f"{change.name}: {rel_path.as_posix()}")
+
if path.name == "setup.toml" and path.parent == self._root:
resource_changes.add(("setup_toml", None))
continue
From 8019db5f920ad346af6f71a99e86d6db1d2b5673 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Wed, 3 Dec 2025 20:10:32 +0800
Subject: [PATCH 06/98] Update utils.py
---
core/utils.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/core/utils.py b/core/utils.py
index 805625344..967eca0b4 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -9,6 +9,7 @@
console = Console()
+levels_str = ["INFO", "WARNING", "ERROR", "CRITICAL"]
def delay(wait=1):
def decorator(func):
From fd252145bf55baa73528c6693df08e77c490e70a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=82=AD=E3=83=A9=E3=83=A1=E3=82=A4?= <1710623513@qq.com>
Date: Tue, 16 Dec 2025 14:56:07 +0800
Subject: [PATCH 07/98] Feat: Add the frontend part
---
service/app.py | 3 +
service/dist/assets/highlight-BsptWXP5.js | 7 +
service/dist/assets/highlight-BsptWXP5.js.br | Bin 0 -> 42983 bytes
service/dist/assets/index-CcNZVzNR.css | 1 +
service/dist/assets/index-CcNZVzNR.css.br | Bin 0 -> 13282 bytes
service/dist/assets/index-DySAOzzf.js | 67 +++
service/dist/assets/index-DySAOzzf.js.br | Bin 0 -> 82223 bytes
service/dist/assets/markdown-XJO5hUeu.js | 29 ++
service/dist/assets/markdown-XJO5hUeu.js.br | Bin 0 -> 41073 bytes
service/dist/assets/misc-GeCjUSSS.js | 344 +++++++++++++
service/dist/assets/misc-GeCjUSSS.js.br | Bin 0 -> 29974 bytes
service/dist/assets/motion-Dp9wOFzY.js | 25 +
service/dist/assets/motion-Dp9wOFzY.js.br | Bin 0 -> 39726 bytes
service/dist/assets/ui-CImlNgPE.js | 53 ++
service/dist/assets/ui-CImlNgPE.js.br | Bin 0 -> 64249 bytes
service/dist/cursors/alternativeselect.cur | Bin 0 -> 503 bytes
service/dist/cursors/areaselect.cur | Bin 0 -> 607 bytes
service/dist/cursors/block.cur | Bin 0 -> 1702 bytes
service/dist/cursors/help.cur | Bin 0 -> 1396 bytes
service/dist/cursors/link.cur | Bin 0 -> 1050 bytes
service/dist/cursors/move.cur | Bin 0 -> 804 bytes
service/dist/cursors/normal.cur | Bin 0 -> 1021 bytes
service/dist/cursors/pen.cur | Bin 0 -> 859 bytes
service/dist/cursors/resizeDIAG1.cur | Bin 0 -> 874 bytes
service/dist/cursors/resizeDIAG2.cur | Bin 0 -> 898 bytes
service/dist/cursors/resizeNS.cur | Bin 0 -> 359 bytes
service/dist/cursors/resizeWE.cur | Bin 0 -> 309 bytes
service/dist/cursors/text_static_blue.cur | Bin 0 -> 406 bytes
service/dist/cursors/text_static_white.cur | Bin 0 -> 391 bytes
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 26 +
.../de_DE/Description of normal graphs.md | 113 +++++
...fficult Chart Configuration Description.md | 102 ++++
.../docs/de_DE/Frontend Development Guide.md | 103 ++++
.../de_DE/Guide for activity sweep fill in.md | 53 ++
... BlueArchive game (first-use must read).md | 30 ++
.../Normal Sweeping Scanning Description.md | 62 +++
.../Scheduling Configuration Filling Guide.md | 83 ++++
...ask force attributes required by region.md | 38 ++
...orting guidelines on issues (important).md | 60 +++
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 24 +
.../en_US/Description of normal graphs.md | 113 +++++
...fficult Chart Configuration Description.md | 103 ++++
.../docs/en_US/Frontend Development Guide.md | 104 ++++
.../en_US/Guide for activity sweep fill in.md | 53 ++
... BlueArchive game (first-use must read).md | 29 ++
.../Normal Sweeping Scanning Description.md | 61 +++
.../Scheduling Configuration Filling Guide.md | 83 ++++
...ask force attributes required by region.md | 37 ++
...orting guidelines on issues (important).md | 57 +++
service/dist/docs/entry.json | 304 ++++++++++++
service/dist/docs/entry.json.br | Bin 0 -> 5361 bytes
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 26 +
.../fr_FR/Description of normal graphs.md | 111 +++++
...fficult Chart Configuration Description.md | 103 ++++
.../docs/fr_FR/Frontend Development Guide.md | 101 ++++
.../fr_FR/Guide for activity sweep fill in.md | 53 ++
... BlueArchive game (first-use must read).md | 30 ++
.../Normal Sweeping Scanning Description.md | 63 +++
.../Scheduling Configuration Filling Guide.md | 85 ++++
...ask force attributes required by region.md | 37 ++
...orting guidelines on issues (important).md | 60 +++
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 28 ++
.../ja_JP/Description of normal graphs.md | 112 +++++
...fficult Chart Configuration Description.md | 102 ++++
.../docs/ja_JP/Frontend Development Guide.md | 104 ++++
.../ja_JP/Guide for activity sweep fill in.md | 50 ++
... BlueArchive game (first-use must read).md | 31 ++
.../Normal Sweeping Scanning Description.md | 64 +++
.../Scheduling Configuration Filling Guide.md | 84 ++++
...ask force attributes required by region.md | 40 ++
...orting guidelines on issues (important).md | 65 +++
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 26 +
.../ko_KR/Description of normal graphs.md | 117 +++++
...fficult Chart Configuration Description.md | 105 ++++
.../docs/ko_KR/Frontend Development Guide.md | 111 +++++
.../ko_KR/Guide for activity sweep fill in.md | 53 ++
... BlueArchive game (first-use must read).md | 32 ++
.../Normal Sweeping Scanning Description.md | 62 +++
.../Scheduling Configuration Filling Guide.md | 86 ++++
...ask force attributes required by region.md | 40 ++
...orting guidelines on issues (important).md | 54 +++
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 25 +
.../ru_RU/Description of normal graphs.md | 114 +++++
...fficult Chart Configuration Description.md | 103 ++++
.../docs/ru_RU/Frontend Development Guide.md | 104 ++++
.../ru_RU/Guide for activity sweep fill in.md | 53 ++
... BlueArchive game (first-use must read).md | 30 ++
.../Normal Sweeping Scanning Description.md | 63 +++
.../Scheduling Configuration Filling Guide.md | 79 +++
...ask force attributes required by region.md | 36 ++
...orting guidelines on issues (important).md | 65 +++
.../Auto clear mainline plot settings.md | 46 ++
.../Commonly used emulator adb address.md | 29 ++
.../zh_CN/Description of normal graphs.md | 111 +++++
...fficult Chart Configuration Description.md | 102 ++++
.../docs/zh_CN/Frontend Development Guide.md | 102 ++++
.../zh_CN/Guide for activity sweep fill in.md | 41 ++
... BlueArchive game (first-use must read).md | 29 ++
.../Normal Sweeping Scanning Description.md | 47 ++
.../Scheduling Configuration Filling Guide.md | 66 +++
...ask force attributes required by region.md | 35 ++
...orting guidelines on issues (important).md | 59 +++
service/dist/favicon.ico | Bin 0 -> 15406 bytes
service/dist/fonts/JetBrainsMono.ttf | Bin 0 -> 189028 bytes
.../dist/fonts/SourceHanSans-Subset-JP.woff2 | Bin 0 -> 258428 bytes
.../dist/fonts/SourceHanSans-Subset-SC.woff2 | Bin 0 -> 266404 bytes
.../dist/icons/property/currency_icon_ap.webp | Bin 0 -> 5976 bytes
.../icons/property/currency_icon_gem.webp | Bin 0 -> 7966 bytes
.../icons/property/currency_icon_gold.webp | Bin 0 -> 9124 bytes
.../icons/property/item_icon_arenacoin.webp | Bin 0 -> 13150 bytes
.../icons/property/item_icon_chasecoin.webp | Bin 0 -> 12830 bytes
.../icons/property/item_icon_craftitem_0.webp | Bin 0 -> 9760 bytes
.../icons/property/item_icon_craftitem_1.webp | Bin 0 -> 15208 bytes
.../dist/icons/property/item_icon_pass.webp | Bin 0 -> 15342 bytes
service/dist/icons/regular/ic_warning.svg | 13 +
service/dist/images/bg-dark.webp | Bin 0 -> 251202 bytes
service/dist/images/bg-light.webp | Bin 0 -> 289662 bytes
service/dist/images/logo.png | Bin 0 -> 126799 bytes
service/dist/index.html | 20 +
service/dist/locales/de.json | 449 +++++++++++++++++
service/dist/locales/de.json.br | Bin 0 -> 6286 bytes
service/dist/locales/en.json | 450 +++++++++++++++++
service/dist/locales/en.json.br | Bin 0 -> 5167 bytes
service/dist/locales/fr.json | 449 +++++++++++++++++
service/dist/locales/fr.json.br | Bin 0 -> 6297 bytes
service/dist/locales/ja.json | 449 +++++++++++++++++
service/dist/locales/ja.json.br | Bin 0 -> 6203 bytes
service/dist/locales/ko.json | 449 +++++++++++++++++
service/dist/locales/ko.json.br | Bin 0 -> 5895 bytes
service/dist/locales/ru.json | 449 +++++++++++++++++
service/dist/locales/ru.json.br | Bin 0 -> 6768 bytes
service/dist/locales/zh.json | 454 ++++++++++++++++++
service/dist/locales/zh.json.br | Bin 0 -> 5534 bytes
service/dist/scripts/fernetBrowser.min.js | 10 +
service/dist/scripts/fernetBrowser.min.js.br | Bin 0 -> 17572 bytes
141 files changed, 9009 insertions(+)
create mode 100644 service/dist/assets/highlight-BsptWXP5.js
create mode 100644 service/dist/assets/highlight-BsptWXP5.js.br
create mode 100644 service/dist/assets/index-CcNZVzNR.css
create mode 100644 service/dist/assets/index-CcNZVzNR.css.br
create mode 100644 service/dist/assets/index-DySAOzzf.js
create mode 100644 service/dist/assets/index-DySAOzzf.js.br
create mode 100644 service/dist/assets/markdown-XJO5hUeu.js
create mode 100644 service/dist/assets/markdown-XJO5hUeu.js.br
create mode 100644 service/dist/assets/misc-GeCjUSSS.js
create mode 100644 service/dist/assets/misc-GeCjUSSS.js.br
create mode 100644 service/dist/assets/motion-Dp9wOFzY.js
create mode 100644 service/dist/assets/motion-Dp9wOFzY.js.br
create mode 100644 service/dist/assets/ui-CImlNgPE.js
create mode 100644 service/dist/assets/ui-CImlNgPE.js.br
create mode 100644 service/dist/cursors/alternativeselect.cur
create mode 100644 service/dist/cursors/areaselect.cur
create mode 100644 service/dist/cursors/block.cur
create mode 100644 service/dist/cursors/help.cur
create mode 100644 service/dist/cursors/link.cur
create mode 100644 service/dist/cursors/move.cur
create mode 100644 service/dist/cursors/normal.cur
create mode 100644 service/dist/cursors/pen.cur
create mode 100644 service/dist/cursors/resizeDIAG1.cur
create mode 100644 service/dist/cursors/resizeDIAG2.cur
create mode 100644 service/dist/cursors/resizeNS.cur
create mode 100644 service/dist/cursors/resizeWE.cur
create mode 100644 service/dist/cursors/text_static_blue.cur
create mode 100644 service/dist/cursors/text_static_white.cur
create mode 100644 service/dist/docs/de_DE/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/de_DE/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/de_DE/Description of normal graphs.md
create mode 100644 service/dist/docs/de_DE/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/de_DE/Frontend Development Guide.md
create mode 100644 service/dist/docs/de_DE/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/de_DE/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/de_DE/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/de_DE/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/de_DE/Task force attributes required by region.md
create mode 100644 service/dist/docs/de_DE/reporting guidelines on issues (important).md
create mode 100644 service/dist/docs/en_US/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/en_US/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/en_US/Description of normal graphs.md
create mode 100644 service/dist/docs/en_US/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/en_US/Frontend Development Guide.md
create mode 100644 service/dist/docs/en_US/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/en_US/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/en_US/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/en_US/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/en_US/Task force attributes required by region.md
create mode 100644 service/dist/docs/en_US/reporting guidelines on issues (important).md
create mode 100644 service/dist/docs/entry.json
create mode 100644 service/dist/docs/entry.json.br
create mode 100644 service/dist/docs/fr_FR/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/fr_FR/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/fr_FR/Description of normal graphs.md
create mode 100644 service/dist/docs/fr_FR/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/fr_FR/Frontend Development Guide.md
create mode 100644 service/dist/docs/fr_FR/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/fr_FR/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/fr_FR/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/fr_FR/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/fr_FR/Task force attributes required by region.md
create mode 100644 service/dist/docs/fr_FR/reporting guidelines on issues (important).md
create mode 100644 service/dist/docs/ja_JP/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/ja_JP/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/ja_JP/Description of normal graphs.md
create mode 100644 service/dist/docs/ja_JP/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/ja_JP/Frontend Development Guide.md
create mode 100644 service/dist/docs/ja_JP/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/ja_JP/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/ja_JP/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/ja_JP/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/ja_JP/Task force attributes required by region.md
create mode 100644 service/dist/docs/ja_JP/reporting guidelines on issues (important).md
create mode 100644 service/dist/docs/ko_KR/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/ko_KR/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/ko_KR/Description of normal graphs.md
create mode 100644 service/dist/docs/ko_KR/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/ko_KR/Frontend Development Guide.md
create mode 100644 service/dist/docs/ko_KR/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/ko_KR/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/ko_KR/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/ko_KR/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/ko_KR/Task force attributes required by region.md
create mode 100644 service/dist/docs/ko_KR/reporting guidelines on issues (important).md
create mode 100644 service/dist/docs/ru_RU/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/ru_RU/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/ru_RU/Description of normal graphs.md
create mode 100644 service/dist/docs/ru_RU/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/ru_RU/Frontend Development Guide.md
create mode 100644 service/dist/docs/ru_RU/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/ru_RU/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/ru_RU/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/ru_RU/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/ru_RU/Task force attributes required by region.md
create mode 100644 service/dist/docs/ru_RU/reporting guidelines on issues (important).md
create mode 100644 service/dist/docs/zh_CN/Auto clear mainline plot settings.md
create mode 100644 service/dist/docs/zh_CN/Commonly used emulator adb address.md
create mode 100644 service/dist/docs/zh_CN/Description of normal graphs.md
create mode 100644 service/dist/docs/zh_CN/Difficult Chart Configuration Description.md
create mode 100644 service/dist/docs/zh_CN/Frontend Development Guide.md
create mode 100644 service/dist/docs/zh_CN/Guide for activity sweep fill in.md
create mode 100644 service/dist/docs/zh_CN/Internal settings in BlueArchive game (first-use must read).md
create mode 100644 service/dist/docs/zh_CN/Normal Sweeping Scanning Description.md
create mode 100644 service/dist/docs/zh_CN/Scheduling Configuration Filling Guide.md
create mode 100644 service/dist/docs/zh_CN/Task force attributes required by region.md
create mode 100644 service/dist/docs/zh_CN/reporting guidelines on issues (important).md
create mode 100644 service/dist/favicon.ico
create mode 100644 service/dist/fonts/JetBrainsMono.ttf
create mode 100644 service/dist/fonts/SourceHanSans-Subset-JP.woff2
create mode 100644 service/dist/fonts/SourceHanSans-Subset-SC.woff2
create mode 100644 service/dist/icons/property/currency_icon_ap.webp
create mode 100644 service/dist/icons/property/currency_icon_gem.webp
create mode 100644 service/dist/icons/property/currency_icon_gold.webp
create mode 100644 service/dist/icons/property/item_icon_arenacoin.webp
create mode 100644 service/dist/icons/property/item_icon_chasecoin.webp
create mode 100644 service/dist/icons/property/item_icon_craftitem_0.webp
create mode 100644 service/dist/icons/property/item_icon_craftitem_1.webp
create mode 100644 service/dist/icons/property/item_icon_pass.webp
create mode 100644 service/dist/icons/regular/ic_warning.svg
create mode 100644 service/dist/images/bg-dark.webp
create mode 100644 service/dist/images/bg-light.webp
create mode 100644 service/dist/images/logo.png
create mode 100644 service/dist/index.html
create mode 100644 service/dist/locales/de.json
create mode 100644 service/dist/locales/de.json.br
create mode 100644 service/dist/locales/en.json
create mode 100644 service/dist/locales/en.json.br
create mode 100644 service/dist/locales/fr.json
create mode 100644 service/dist/locales/fr.json.br
create mode 100644 service/dist/locales/ja.json
create mode 100644 service/dist/locales/ja.json.br
create mode 100644 service/dist/locales/ko.json
create mode 100644 service/dist/locales/ko.json.br
create mode 100644 service/dist/locales/ru.json
create mode 100644 service/dist/locales/ru.json.br
create mode 100644 service/dist/locales/zh.json
create mode 100644 service/dist/locales/zh.json.br
create mode 100644 service/dist/scripts/fernetBrowser.min.js
create mode 100644 service/dist/scripts/fernetBrowser.min.js.br
diff --git a/service/app.py b/service/app.py
index 4d2a88cee..48f64ef30 100644
--- a/service/app.py
+++ b/service/app.py
@@ -10,6 +10,7 @@
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
from starlette.middleware.cors import CORSMiddleware
+from starlette.staticfiles import StaticFiles
from .context import ServiceContext
from .encryption import AuthenticationError, CipherBox, HandshakeResponse, HandshakeSession
@@ -362,3 +363,5 @@ async def websocket_heartbeat(websocket: WebSocket) -> None:
task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await task
+
+app.mount("/", StaticFiles(directory="service/dist", html=True), name="static")
diff --git a/service/dist/assets/highlight-BsptWXP5.js b/service/dist/assets/highlight-BsptWXP5.js
new file mode 100644
index 000000000..04898bf2d
--- /dev/null
+++ b/service/dist/assets/highlight-BsptWXP5.js
@@ -0,0 +1,7 @@
+import{g as Wn}from"./motion-Dp9wOFzY.js";function Wr(){}function Yr(){}const mt=(function(e){if(e==null)return Vn;if(typeof e=="function")return Xe(e);if(typeof e=="object")return Array.isArray(e)?Yn(e):Zn(e);if(typeof e=="string")return Xn(e);throw new Error("Expected function, string, or object as test")});function Yn(e){const t=[];let i=-1;for(;++i":""))+")"})}return p;function p(){let f=Zt,N,_,y;if((!t||d(l,c,g[g.length-1]||void 0))&&(f=ti(i(l,g)),f[0]===Dt))return f;if("children"in l&&l.children){const w=l;if(w.children&&f[0]!==jn)for(_=(r?w.children.length:-1)+o,y=g.concat(w);_>-1&&_c&&(c=g):g&&(c!==void 0&&c>-1&&l.push(`
+`.repeat(c)||" "),c=-1,l.push(g))}return l.join("")}function Vt(e,t,i){return e.type==="element"?di(e,t,i):e.type==="text"?i.whitespace==="normal"?Qt(e,i):ui(e):[]}function di(e,t,i){const r=Jt(e,i),a=e.children||[];let d=-1,o=[];if(ci(e))return o;let s,l;for(bt(e)||$t(e)&&Bt(t,e,$t)?l=`
+`:si(e)?(s=2,l=2):Xt(e)&&(s=1,l=1);++d]+>")+")",s={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},c={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+"\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)"+"|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},g={className:"number",variants:[{begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)"},{begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)"}],relevance:0},m={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(c,{className:"string"}),{className:"string",begin:/<.*?>/},i,e.C_BLOCK_COMMENT_MODE]},p={className:"title",begin:t.optional(a)+e.IDENT_RE,relevance:0},f=t.optional(a)+e.IDENT_RE+"\\s*\\(",N=["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],_=["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],y=["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","flat_map","flat_set","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"],w=["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"],k={type:_,keyword:N,literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:y},D={className:"function.dispatch",relevance:0,keywords:{_hint:w},begin:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/))},x=[D,m,s,i,e.C_BLOCK_COMMENT_MODE,g,c],q={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:k,contains:x.concat([{begin:/\(/,end:/\)/,keywords:k,contains:x.concat(["self"]),relevance:0}]),relevance:0},z={className:"function",begin:"("+o+"[\\*&\\s]+)+"+f,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:k,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:k,relevance:0},{begin:f,returnBegin:!0,contains:[p],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[c,g]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:k,relevance:0,contains:[i,e.C_BLOCK_COMMENT_MODE,c,g,s,{begin:/\(/,end:/\)/,keywords:k,relevance:0,contains:["self",i,e.C_BLOCK_COMMENT_MODE,c,g,s]}]},s,i,e.C_BLOCK_COMMENT_MODE,m]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:k,illegal:"",classNameAliases:{"function.dispatch":"built_in"},contains:[].concat(q,z,D,x,[m,{begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function|flat_map|flat_set)\\s*<(?!<)",end:">",keywords:k,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:k},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}}function _i(e){const t={type:["boolean","byte","word","String"],built_in:["KeyboardController","MouseController","SoftwareSerial","EthernetServer","EthernetClient","LiquidCrystal","RobotControl","GSMVoiceCall","EthernetUDP","EsploraTFT","HttpClient","RobotMotor","WiFiClient","GSMScanner","FileSystem","Scheduler","GSMServer","YunClient","YunServer","IPAddress","GSMClient","GSMModem","Keyboard","Ethernet","Console","GSMBand","Esplora","Stepper","Process","WiFiUDP","GSM_SMS","Mailbox","USBHost","Firmata","PImage","Client","Server","GSMPIN","FileIO","Bridge","Serial","EEPROM","Stream","Mouse","Audio","Servo","File","Task","GPRS","WiFi","Wire","TFT","GSM","SPI","SD"],_hints:["setup","loop","runShellCommandAsynchronously","analogWriteResolution","retrieveCallingNumber","printFirmwareVersion","analogReadResolution","sendDigitalPortPair","noListenOnLocalhost","readJoystickButton","setFirmwareVersion","readJoystickSwitch","scrollDisplayRight","getVoiceCallStatus","scrollDisplayLeft","writeMicroseconds","delayMicroseconds","beginTransmission","getSignalStrength","runAsynchronously","getAsynchronously","listenOnLocalhost","getCurrentCarrier","readAccelerometer","messageAvailable","sendDigitalPorts","lineFollowConfig","countryNameWrite","runShellCommand","readStringUntil","rewindDirectory","readTemperature","setClockDivider","readLightSensor","endTransmission","analogReference","detachInterrupt","countryNameRead","attachInterrupt","encryptionType","readBytesUntil","robotNameWrite","readMicrophone","robotNameRead","cityNameWrite","userNameWrite","readJoystickY","readJoystickX","mouseReleased","openNextFile","scanNetworks","noInterrupts","digitalWrite","beginSpeaker","mousePressed","isActionDone","mouseDragged","displayLogos","noAutoscroll","addParameter","remoteNumber","getModifiers","keyboardRead","userNameRead","waitContinue","processInput","parseCommand","printVersion","readNetworks","writeMessage","blinkVersion","cityNameRead","readMessage","setDataMode","parsePacket","isListening","setBitOrder","beginPacket","isDirectory","motorsWrite","drawCompass","digitalRead","clearScreen","serialEvent","rightToLeft","setTextSize","leftToRight","requestFrom","keyReleased","compassRead","analogWrite","interrupts","WiFiServer","disconnect","playMelody","parseFloat","autoscroll","getPINUsed","setPINUsed","setTimeout","sendAnalog","readSlider","analogRead","beginWrite","createChar","motorsStop","keyPressed","tempoWrite","readButton","subnetMask","debugPrint","macAddress","writeGreen","randomSeed","attachGPRS","readString","sendString","remotePort","releaseAll","mouseMoved","background","getXChange","getYChange","answerCall","getResult","voiceCall","endPacket","constrain","getSocket","writeJSON","getButton","available","connected","findUntil","readBytes","exitValue","readGreen","writeBlue","startLoop","IPAddress","isPressed","sendSysex","pauseMode","gatewayIP","setCursor","getOemKey","tuneWrite","noDisplay","loadImage","switchPIN","onRequest","onReceive","changePIN","playFile","noBuffer","parseInt","overflow","checkPIN","knobRead","beginTFT","bitClear","updateIR","bitWrite","position","writeRGB","highByte","writeRed","setSpeed","readBlue","noStroke","remoteIP","transfer","shutdown","hangCall","beginSMS","endWrite","attached","maintain","noCursor","checkReg","checkPUK","shiftOut","isValid","shiftIn","pulseIn","connect","println","localIP","pinMode","getIMEI","display","noBlink","process","getBand","running","beginSD","drawBMP","lowByte","setBand","release","bitRead","prepare","pointTo","readRed","setMode","noFill","remove","listen","stroke","detach","attach","noTone","exists","buffer","height","bitSet","circle","config","cursor","random","IRread","setDNS","endSMS","getKey","micros","millis","begin","print","write","ready","flush","width","isPIN","blink","clear","press","mkdir","rmdir","close","point","yield","image","BSSID","click","delay","read","text","move","peek","beep","rect","line","open","seek","fill","size","turn","stop","home","find","step","tone","sqrt","RSSI","SSID","end","bit","tan","cos","sin","pow","map","abs","max","min","get","run","put"],literal:["DIGITAL_MESSAGE","FIRMATA_STRING","ANALOG_MESSAGE","REPORT_DIGITAL","REPORT_ANALOG","INPUT_PULLUP","SET_PIN_MODE","INTERNAL2V56","SYSTEM_RESET","LED_BUILTIN","INTERNAL1V1","SYSEX_START","INTERNAL","EXTERNAL","DEFAULT","OUTPUT","INPUT","HIGH","LOW"]},i=fi(e),r=i.keywords;return r.type=[...r.type,...t.type],r.literal=[...r.literal,...t.literal],r.built_in=[...r.built_in,...t.built_in],r._hints=t._hints,i.name="Arduino",i.aliases=["ino"],i.supersetOf="cpp",i}function Ei(e){const t=e.regex,i={},r={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[i]}]};Object.assign(i,{className:"variable",variants:[{begin:t.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},r]});const a={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},d=e.inherit(e.COMMENT(),{match:[/(^|\s)/,/#.*$/],scope:{2:"comment"}}),o={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,i,a]};a.contains.push(s);const l={match:/\\"/},c={className:"string",begin:/'/,end:/'/},g={match:/\\'/},m={begin:/\$?\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,i]},p=["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"],f=e.SHEBANG({binary:`(${p.join("|")})`,relevance:10}),N={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0},_=["if","then","else","elif","fi","time","for","while","until","in","do","done","case","esac","coproc","function","select"],y=["true","false"],w={match:/(\/[a-z._-]+)+/},I=["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset"],L=["alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","sudo","type","typeset","ulimit","unalias"],k=["autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp"],D=["chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"];return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,keyword:_,literal:y,built_in:[...I,...L,"set","shopt",...k,...D]},contains:[f,e.SHEBANG(),N,m,d,o,w,s,l,c,g,i]}}function hi(e){const t=e.regex,i=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),r="decltype\\(auto\\)",a="[a-zA-Z_]\\w*::",o="("+r+"|"+t.optional(a)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",s={className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{match:/\batomic_[a-z]{3,6}\b/}]},c={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+"\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)"+"|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},g={className:"number",variants:[{match:/\b(0b[01']+)/},{match:/(-?)\b([\d']+(\.[\d']*)?|\.[\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)/},{match:/(-?)\b(0[xX][a-fA-F0-9]+(?:'[a-fA-F0-9]+)*(?:\.[a-fA-F0-9]*(?:'[a-fA-F0-9]*)*)?(?:[pP][-+]?[0-9]+)?(l|L)?(u|U)?)/},{match:/(-?)\b\d+(?:'\d+)*(?:\.\d*(?:'\d*)*)?(?:[eE][-+]?\d+)?/}],relevance:0},m={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(c,{className:"string"}),{className:"string",begin:/<.*?>/},i,e.C_BLOCK_COMMENT_MODE]},p={className:"title",begin:t.optional(a)+e.IDENT_RE,relevance:0},f=t.optional(a)+e.IDENT_RE+"\\s*\\(",y={keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","typeof","typeof_unqual","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_BitInt","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal96","_Decimal128","_Decimal64x","_Decimal128x","_Float16","_Float32","_Float64","_Float128","_Float32x","_Float64x","_Float128x","const","static","constexpr","complex","bool","imaginary"],literal:"true false NULL",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"},w=[m,s,i,e.C_BLOCK_COMMENT_MODE,g,c],I={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:y,contains:w.concat([{begin:/\(/,end:/\)/,keywords:y,contains:w.concat(["self"]),relevance:0}]),relevance:0},L={begin:"("+o+"[\\*&\\s]+)+"+f,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:y,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:y,relevance:0},{begin:f,returnBegin:!0,contains:[e.inherit(p,{className:"title.function"})],relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:y,relevance:0,contains:[i,e.C_BLOCK_COMMENT_MODE,c,g,s,{begin:/\(/,end:/\)/,keywords:y,relevance:0,contains:["self",i,e.C_BLOCK_COMMENT_MODE,c,g,s]}]},s,i,e.C_BLOCK_COMMENT_MODE,m]};return{name:"C",aliases:["h"],keywords:y,disableAutodetect:!0,illegal:"",contains:[].concat(I,L,w,[m,{begin:e.IDENT_RE+"::",keywords:y},{className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/,contains:[{beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:m,strings:c,keywords:y}}}function Ni(e){const t=e.regex,i=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),r="decltype\\(auto\\)",a="[a-zA-Z_]\\w*::",o="(?!struct)("+r+"|"+t.optional(a)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",s={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},c={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+"\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)"+"|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},g={className:"number",variants:[{begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)"},{begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)"}],relevance:0},m={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(c,{className:"string"}),{className:"string",begin:/<.*?>/},i,e.C_BLOCK_COMMENT_MODE]},p={className:"title",begin:t.optional(a)+e.IDENT_RE,relevance:0},f=t.optional(a)+e.IDENT_RE+"\\s*\\(",N=["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],_=["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],y=["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","flat_map","flat_set","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"],w=["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"],k={type:_,keyword:N,literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:y},D={className:"function.dispatch",relevance:0,keywords:{_hint:w},begin:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/))},x=[D,m,s,i,e.C_BLOCK_COMMENT_MODE,g,c],q={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:k,contains:x.concat([{begin:/\(/,end:/\)/,keywords:k,contains:x.concat(["self"]),relevance:0}]),relevance:0},z={className:"function",begin:"("+o+"[\\*&\\s]+)+"+f,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:k,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:k,relevance:0},{begin:f,returnBegin:!0,contains:[p],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[c,g]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:k,relevance:0,contains:[i,e.C_BLOCK_COMMENT_MODE,c,g,s,{begin:/\(/,end:/\)/,keywords:k,relevance:0,contains:["self",i,e.C_BLOCK_COMMENT_MODE,c,g,s]}]},s,i,e.C_BLOCK_COMMENT_MODE,m]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:k,illegal:"",classNameAliases:{"function.dispatch":"built_in"},contains:[].concat(q,z,D,x,[m,{begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function|flat_map|flat_set)\\s*<(?!<)",end:">",keywords:k,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:k},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}}function yi(e){const t=["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"],i=["public","private","protected","static","internal","protected","abstract","async","extern","override","unsafe","virtual","new","sealed","partial"],r=["default","false","null","true"],a=["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"],d=["add","alias","and","ascending","args","async","await","by","descending","dynamic","equals","file","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","record","remove","required","scoped","select","set","unmanaged","value|0","var","when","where","with","yield"],o={keyword:a.concat(d),built_in:t,literal:r},s=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),l={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},c={className:"string",begin:/"""("*)(?!")(.|\n)*?"""\1/,relevance:1},g={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},m=e.inherit(g,{illegal:/\n/}),p={className:"subst",begin:/\{/,end:/\}/,keywords:o},f=e.inherit(p,{illegal:/\n/}),N={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},e.BACKSLASH_ESCAPE,f]},_={className:"string",begin:/\$@"/,end:'"',contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},p]},y=e.inherit(_,{illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},f]});p.contains=[_,N,g,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,l,e.C_BLOCK_COMMENT_MODE],f.contains=[y,N,m,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,l,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];const w={variants:[c,_,N,g,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},I={begin:"<",end:">",contains:[{beginKeywords:"in out"},s]},L=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",k={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:o,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:""},{begin:"?",end:">"}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{keyword:"if else elif endif define undef warning error line region endregion pragma checksum"}},w,l,{beginKeywords:"class interface",relevance:0,end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},s,I,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[s,I,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+L+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:o,contains:[{beginKeywords:i.join(" "),relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,contains:[e.TITLE_MODE,I],relevance:0},{match:/\(\)/},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,relevance:0,contains:[w,l,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},k]}}const Ti=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),wi=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],Si=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],vi=[...wi,...Si],Ai=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),Oi=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),Ri=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),ki=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse();function Mi(e){const t=e.regex,i=Ti(e),r={begin:/-(webkit|moz|ms|o)-(?=[a-z])/},a="and or not only",d=/@-?\w[\w]*(-\w+)*/,o="[a-zA-Z-][a-zA-Z0-9_-]*",s=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[i.BLOCK_COMMENT,r,i.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:"selector-class",begin:"\\."+o,relevance:0},i.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+Oi.join("|")+")"},{begin:":(:)?("+Ri.join("|")+")"}]},i.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ki.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[i.BLOCK_COMMENT,i.HEXCOLOR,i.IMPORTANT,i.CSS_NUMBER_MODE,...s,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[...s,{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},i.FUNCTION_DISPATCH]},{begin:t.lookahead(/@/),end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",begin:d},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:a,attribute:Ai.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...s,i.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+vi.join("|")+")\\b"}]}}function Ii(e){const t=e.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:t.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:t.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}}function xi(e){const d={keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"],type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],literal:["true","false","iota","nil"],built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]};return{name:"Go",aliases:["golang"],keywords:d,illegal:"",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:"`",end:"`"}]},{className:"number",variants:[{match:/-?\b0[xX]\.[a-fA-F0-9](_?[a-fA-F0-9])*[pP][+-]?\d(_?\d)*i?/,relevance:0},{match:/-?\b0[xX](_?[a-fA-F0-9])+((\.([a-fA-F0-9](_?[a-fA-F0-9])*)?)?[pP][+-]?\d(_?\d)*)?i?/,relevance:0},{match:/-?\b0[oO](_?[0-7])*i?/,relevance:0},{match:/-?\.\d(_?\d)*([eE][+-]?\d(_?\d)*)?i?/,relevance:0},{match:/-?\b\d(_?\d)*(\.(\d(_?\d)*)?)?([eE][+-]?\d(_?\d)*)?i?/,relevance:0}]},{begin:/:=/},{className:"function",beginKeywords:"func",end:"\\s*(\\{|$)",excludeEnd:!0,contains:[e.TITLE_MODE,{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:d,illegal:/["']/}]}]}}function Ci(e){const t=e.regex,i=/[_A-Za-z][_0-9A-Za-z]*/;return{name:"GraphQL",aliases:["gql"],case_insensitive:!0,disableAutodetect:!1,keywords:{keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"],literal:["true","false","null"]},contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation",begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/,end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{scope:"symbol",begin:t.concat(i,t.lookahead(/\s*:/)),relevance:0}],illegal:[/[;<']/,/BEGIN/]}}function Li(e){const t=e.regex,i={className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}]},r=e.COMMENT();r.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const a={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/}]},d={className:"literal",begin:/\bon|off|true|false|yes|no\b/},o={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},s={begin:/\[/,end:/\]/,contains:[r,d,a,o,i,"self"],relevance:0},l=/[A-Za-z0-9_-]+/,c=/"(\\"|[^"])*"/,g=/'[^']*'/,m=t.either(l,c,g),p=t.concat(m,"(\\s*\\.\\s*",m,")*",t.lookahead(/\s*=\s*[^#\s]/));return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[r,{className:"section",begin:/\[+/,end:/\]+/},{begin:p,className:"attr",starts:{end:/$/,contains:[r,s,d,a,o,i]}}]}}var ke="[0-9](_*[0-9])*",Ge=`\\.(${ke})`,Ke="[0-9a-fA-F](_*[0-9a-fA-F])*",zt={className:"number",variants:[{begin:`(\\b(${ke})((${Ge})|\\.)?|(${Ge}))[eE][+-]?(${ke})[fFdD]?\\b`},{begin:`\\b(${ke})((${Ge})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${Ge})[fFdD]?\\b`},{begin:`\\b(${ke})[fFdD]\\b`},{begin:`\\b0[xX]((${Ke})\\.?|(${Ke})?\\.(${Ke}))[pP][+-]?(${ke})[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${Ke})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function jt(e,t,i){return i===-1?"":e.replace(t,r=>jt(e,t,i-1))}function Di(e){const t=e.regex,i="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",r=i+jt("(?:<"+i+"~~~(?:\\s*,\\s*"+i+"~~~)*>)?",/~~~/g,2),l={keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits","goto","when"],literal:["false","true","null"],type:["char","boolean","long","float","int","byte","short","double"],built_in:["super","this"]},c={className:"meta",begin:"@"+i,contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},g={className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:"Java",aliases:["jsp"],keywords:l,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{begin:/import java\.[a-z]+\./,keywords:"import",relevance:2},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/,className:"string",contains:[e.BACKSLASH_ESCAPE]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,i],className:{1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{begin:[t.concat(/(?!else)/,i),/\s+/,i,/\s+/,/=(?!=)/],className:{1:"type",3:"variable",5:"operator"}},{begin:[/record/,/\s+/,i],className:{1:"keyword",3:"title.class"},contains:[g,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"new throw return else",relevance:0},{begin:["(?:"+r+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{2:"title.function"},keywords:l,contains:[{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[c,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,zt,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},zt,c]}}const Gt="[A-Za-z$_][0-9A-Za-z$_]*",Bi=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],Pi=["true","false","null","undefined","NaN","Infinity"],en=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],tn=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],nn=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],Ui=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],Fi=[].concat(nn,en,tn);function $i(e){const t=e.regex,i=(P,{after:j})=>{const ae=""+P[0].slice(1);return P.input.indexOf(ae,j)!==-1},r=Gt,a={begin:"<>",end:">"},d=/<[A-Za-z0-9\\._:-]+\s*\/>/,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(P,j)=>{const ae=P[0].length+P.index,ne=P.input[ae];if(ne==="<"||ne===","){j.ignoreMatch();return}ne===">"&&(i(P,{after:ae})||j.ignoreMatch());let ee;const _e=P.input.substring(ae);if(ee=_e.match(/^\s*=/)){j.ignoreMatch();return}if((ee=_e.match(/^\s+extends\s+/))&&ee.index===0){j.ignoreMatch();return}}},s={$pattern:Gt,keyword:Bi,literal:Pi,built_in:Fi,"variable.language":Ui},l="[0-9](_?[0-9])*",c=`\\.(${l})`,g="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",m={className:"number",variants:[{begin:`(\\b(${g})((${c})|\\.)?|(${c}))[eE][+-]?(${l})\\b`},{begin:`\\b(${g})\\b((${c})\\b|\\.)?|(${c})\\b`},{begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{begin:"\\b0[0-7]+n?\\b"}],relevance:0},p={className:"subst",begin:"\\$\\{",end:"\\}",keywords:s,contains:[]},f={begin:".?html`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,p],subLanguage:"xml"}},N={begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,p],subLanguage:"css"}},_={begin:".?gql`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,p],subLanguage:"graphql"}},y={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,p]},I={className:"comment",variants:[e.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:"variable",begin:r+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},L=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,f,N,_,y,{match:/\$\d+/},m];p.contains=L.concat({begin:/\{/,end:/\}/,keywords:s,contains:["self"].concat(L)});const k=[].concat(I,p.contains),D=k.concat([{begin:/(\s*)\(/,end:/\)/,keywords:s,contains:["self"].concat(k)}]),x={className:"params",begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:D},q={variants:[{match:[/class/,/\s+/,r,/\s+/,/extends/,/\s+/,t.concat(r,"(",t.concat(/\./,r),")*")],scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{match:[/class/,/\s+/,r],scope:{1:"keyword",3:"title.class"}}]},z={relevance:0,match:t.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:"title.class",keywords:{_:[...en,...tn]}},H={label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},se={variants:[{match:[/function/,/\s+/,r,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:"keyword",3:"title.function"},label:"func.def",contains:[x],illegal:/%/},de={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"};function le(P){return t.concat("(?!",P.join("|"),")")}const ue={match:t.concat(/\b/,le([...nn,"super","import"].map(P=>`${P}\\s*\\(`)),r,t.lookahead(/\s*\(/)),className:"title.function",relevance:0},W={begin:t.concat(/\./,t.lookahead(t.concat(r,/(?![0-9A-Za-z$_(])/))),end:r,excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},V={match:[/get|set/,/\s+/,r,/(?=\()/],className:{1:"keyword",3:"title.function"},contains:[{begin:/\(\)/},x]},re="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+e.UNDERSCORE_IDENT_RE+")\\s*=>",fe={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(re)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[x]};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:s,exports:{PARAMS_CONTAINS:D,CLASS_REFERENCE:z},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),H,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,f,N,_,y,I,{match:/\$\d+/},m,z,{scope:"attr",match:r+t.lookahead(":"),relevance:0},fe,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[I,e.REGEXP_MODE,{className:"function",begin:re,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:D}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:a.begin,end:a.end},{match:d},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,contains:["self"]}]}]},se,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[x,e.inherit(e.TITLE_MODE,{begin:r,className:"title.function"})]},{match:/\.\.\./,relevance:0},W,{match:"\\$"+r,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[x]},ue,de,q,V,{match:/\$[(.]/}]}}function zi(e){const t={className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},i={match:/[{}[\],:]/,className:"punctuation",relevance:0},r=["true","false","null"],a={scope:"literal",beginKeywords:r.join(" ")};return{name:"JSON",aliases:["jsonc"],keywords:{literal:r},contains:[t,i,e.QUOTE_STRING_MODE,a,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}}var Me="[0-9](_*[0-9])*",He=`\\.(${Me})`,qe="[0-9a-fA-F](_*[0-9a-fA-F])*",Gi={className:"number",variants:[{begin:`(\\b(${Me})((${He})|\\.)?|(${He}))[eE][+-]?(${Me})[fFdD]?\\b`},{begin:`\\b(${Me})((${He})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${He})[fFdD]?\\b`},{begin:`\\b(${Me})[fFdD]\\b`},{begin:`\\b0[xX]((${qe})\\.?|(${qe})?\\.(${qe}))[pP][+-]?(${Me})[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${qe})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function Ki(e){const t={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},i={className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},r={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},d={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},o={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[d,a]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,d,a]}]};a.contains.push(o);const s={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(o,{className:"string"}),"self"]}]},c=Gi,g=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),m={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},p=m;return p.variants[1].contains=[m],m.variants[1].contains=[p],{name:"Kotlin",aliases:["kt","kts"],keywords:t,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,g,i,r,s,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:t,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin:/,end:/>/,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:t,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[m,e.C_LINE_COMMENT_MODE,g],relevance:0},e.C_LINE_COMMENT_MODE,g,s,l,o,e.C_NUMBER_MODE]},g]},{begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin:/,end:/>/,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/,excludeBegin:!0,returnEnd:!0},s,l]},o,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:`
+`},c]}}const Hi=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),qi=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],Wi=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],Yi=[...qi,...Wi],Zi=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),rn=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),an=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),Xi=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse(),Vi=rn.concat(an).sort().reverse();function Qi(e){const t=Hi(e),i=Vi,r="and or not only",a="[\\w-]+",d="("+a+"|@\\{"+a+"\\})",o=[],s=[],l=function(L){return{className:"string",begin:"~?"+L+".*?"+L}},c=function(L,k,D){return{className:L,begin:k,relevance:D}},g={$pattern:/[a-z-]+/,keyword:r,attribute:Zi.join(" ")},m={begin:"\\(",end:"\\)",contains:s,keywords:g,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l("'"),l('"'),t.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},t.HEXCOLOR,m,c("variable","@@?"+a,10),c("variable","@\\{"+a+"\\}"),c("built_in","~?`[^`]*?`"),{className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0},t.IMPORTANT,{beginKeywords:"and not"},t.FUNCTION_DISPATCH);const p=s.concat({begin:/\{/,end:/\}/,contains:o}),f={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},N={begin:d+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+Xi.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},_={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",keywords:g,returnEnd:!0,contains:s,relevance:0}},y={className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a}],starts:{end:"[;}]",returnEnd:!0,contains:p}},w={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:d,end:/\{/}],returnBegin:!0,returnEnd:!0,illegal:`[<='$"]`,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,f,c("keyword","all\\b"),c("variable","@\\{"+a+"\\}"),{begin:"\\b("+Yi.join("|")+")\\b",className:"selector-tag"},t.CSS_NUMBER_MODE,c("selector-tag",d,0),c("selector-id","#"+d),c("selector-class","\\."+d,0),c("selector-tag","&",0),t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",begin:":("+rn.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+an.join("|")+")"},{begin:/\(/,end:/\)/,relevance:0,contains:p},{begin:"!important"},t.FUNCTION_DISPATCH]},I={begin:a+`:(:)?(${i.join("|")})`,returnBegin:!0,contains:[w]};return o.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,_,y,I,N,w,f,t.FUNCTION_DISPATCH),{name:"Less",case_insensitive:!0,illegal:`[=>'/<($"]`,contains:o}}function Ji(e){const t="\\[=*\\[",i="\\]=*\\]",r={begin:t,end:i,contains:["self"]},a=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,i,{contains:[r],relevance:10})];return{name:"Lua",aliases:["pluto"],keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:t,end:i,contains:[r],relevance:5}])}}function ji(e){const t={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%\^\+\*]/}]},i={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,t]},r={className:"variable",begin:/\$\([\w-]+\s/,end:/\)/,keywords:{built_in:"subst patsubst strip findstring filter filter-out sort word wordlist firstword lastword dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath error warning shell origin flavor foreach if or and call eval file value"},contains:[t,i]},a={begin:"^"+e.UNDERSCORE_IDENT_RE+"\\s*(?=[:+?]?=)"},d={className:"meta",begin:/^\.PHONY:/,end:/$/,keywords:{$pattern:/[\.\w]+/,keyword:".PHONY"}},o={className:"section",begin:/^[^\s]+:/,end:/$/,contains:[t]};return{name:"Makefile",aliases:["mk","mak","make"],keywords:{$pattern:/[\w-]+/,keyword:"define endef undefine ifdef ifndef ifeq ifneq else endif include -include sinclude override export unexport private vpath"},contains:[e.HASH_COMMENT_MODE,t,i,r,a,d,o]}}function er(e){const t=e.regex,i={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},r={begin:"^[-\\*]{3,}",end:"$"},a={className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},d={className:"bullet",begin:"^[ ]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},o={begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},s=/[A-Za-z][A-Za-z0-9+.-]*/,l={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,relevance:2},{begin:t.concat(/\[.+?\]\(/,s,/:\/\/.*?\)/),relevance:2},{begin:/\[.+?\]\([./?].*?\)/,relevance:1},{begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}]},c={className:"strong",contains:[],variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]},g={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{begin:/_(?![_\s])/,end:/_/,relevance:0}]},m=e.inherit(c,{contains:[]}),p=e.inherit(g,{contains:[]});c.contains.push(p),g.contains.push(m);let f=[i,l];return[c,g,m,p].forEach(w=>{w.contains=w.contains.concat(f)}),f=f.concat(c,g),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:f},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:f}]}]},i,d,c,g,{className:"quote",begin:"^>\\s+",contains:f,end:"$"},a,r,l,o,{scope:"literal",match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}function tr(e){const t={className:"built_in",begin:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},i=/[a-zA-Z@][a-zA-Z0-9_]*/,s={"variable.language":["this","super"],$pattern:i,keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"],literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"],built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"],type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"]},l={$pattern:i,keyword:["@interface","@class","@protocol","@implementation"]};return{name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"],keywords:s,illegal:"",contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.C_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"string",variants:[{begin:'@"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]}]},{className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),{className:"string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+l.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:l,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}function nr(e){const t=e.regex,i=["abs","accept","alarm","and","atan2","bind","binmode","bless","break","caller","chdir","chmod","chomp","chop","chown","chr","chroot","class","close","closedir","connect","continue","cos","crypt","dbmclose","dbmopen","defined","delete","die","do","dump","each","else","elsif","endgrent","endhostent","endnetent","endprotoent","endpwent","endservent","eof","eval","exec","exists","exit","exp","fcntl","field","fileno","flock","for","foreach","fork","format","formline","getc","getgrent","getgrgid","getgrnam","gethostbyaddr","gethostbyname","gethostent","getlogin","getnetbyaddr","getnetbyname","getnetent","getpeername","getpgrp","getpriority","getprotobyname","getprotobynumber","getprotoent","getpwent","getpwnam","getpwuid","getservbyname","getservbyport","getservent","getsockname","getsockopt","given","glob","gmtime","goto","grep","gt","hex","if","index","int","ioctl","join","keys","kill","last","lc","lcfirst","length","link","listen","local","localtime","log","lstat","lt","ma","map","method","mkdir","msgctl","msgget","msgrcv","msgsnd","my","ne","next","no","not","oct","open","opendir","or","ord","our","pack","package","pipe","pop","pos","print","printf","prototype","push","q|0","qq","quotemeta","qw","qx","rand","read","readdir","readline","readlink","readpipe","recv","redo","ref","rename","require","reset","return","reverse","rewinddir","rindex","rmdir","say","scalar","seek","seekdir","select","semctl","semget","semop","send","setgrent","sethostent","setnetent","setpgrp","setpriority","setprotoent","setpwent","setservent","setsockopt","shift","shmctl","shmget","shmread","shmwrite","shutdown","sin","sleep","socket","socketpair","sort","splice","split","sprintf","sqrt","srand","stat","state","study","sub","substr","symlink","syscall","sysopen","sysread","sysseek","system","syswrite","tell","telldir","tie","tied","time","times","tr","truncate","uc","ucfirst","umask","undef","unless","unlink","unpack","unshift","untie","until","use","utime","values","vec","wait","waitpid","wantarray","warn","when","while","write","x|0","xor","y|0"],r=/[dualxmsipngr]{0,12}/,a={$pattern:/[\w.]+/,keyword:i.join(" ")},d={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},o={begin:/->\{/,end:/\}/},s={scope:"attr",match:/\s+:\s*\w+(\s*\(.*?\))?/},l={scope:"variable",variants:[{begin:/\$\d/},{begin:t.concat(/[$%@](?!")(\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")},{begin:/[$%@](?!")[^\s\w{=]|\$=/,relevance:0}],contains:[s]},c={className:"number",variants:[{match:/0?\.[0-9][0-9_]+\b/},{match:/\bv?(0|[1-9][0-9_]*(\.[0-9_]+)?|[1-9][0-9_]*)\b/},{match:/\b0[0-7][0-7_]*\b/},{match:/\b0x[0-9a-fA-F][0-9a-fA-F_]*\b/},{match:/\b0b[0-1][0-1_]*\b/}],relevance:0},g=[e.BACKSLASH_ESCAPE,d,l],m=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],p=(_,y,w="\\1")=>{const I=w==="\\1"?w:t.concat(w,y);return t.concat(t.concat("(?:",_,")"),y,/(?:\\.|[^\\\/])*?/,I,/(?:\\.|[^\\\/])*?/,w,r)},f=(_,y,w)=>t.concat(t.concat("(?:",_,")"),y,/(?:\\.|[^\\\/])*?/,w,r),N=[l,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{endsWithParent:!0}),o,{className:"string",contains:g,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{begin:"-?\\w+\\s*=>",relevance:0}]},c,{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{begin:p("s|tr|y",t.either(...m,{capture:!0}))},{begin:p("s|tr|y","\\(","\\)")},{begin:p("s|tr|y","\\[","\\]")},{begin:p("s|tr|y","\\{","\\}")}],relevance:2},{className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{begin:f("(?:m|qr)?",/\//,/\//)},{begin:f("m|qr",t.either(...m,{capture:!0}),/\1/)},{begin:f("m|qr",/\(/,/\)/)},{begin:f("m|qr",/\[/,/\]/)},{begin:f("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub method",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE,s]},{className:"class",beginKeywords:"class",end:"[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE,s,c]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return d.contains=N,o.contains=N,{name:"Perl",aliases:["pl","pm"],keywords:a,contains:N}}function ir(e){const t=e.regex,i=/(?![A-Za-z0-9])(?![$])/,r=t.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,i),a=t.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,i),d=t.concat(/[A-Z]+/,i),o={scope:"variable",match:"\\$+"+r},s={scope:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{begin:/\?>/}]},l={scope:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]},c=e.inherit(e.APOS_STRING_MODE,{illegal:null}),g=e.inherit(e.QUOTE_STRING_MODE,{illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(l)}),m={begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/,contains:e.QUOTE_STRING_MODE.contains.concat(l),"on:begin":(W,V)=>{V.data._beginMatch=W[1]||W[2]},"on:end":(W,V)=>{V.data._beginMatch!==W[1]&&V.ignoreMatch()}},p=e.END_SAME_AS_BEGIN({begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/}),f=`[
+]`,N={scope:"string",variants:[g,c,m,p]},_={scope:"number",variants:[{begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?"}],relevance:0},y=["false","null","true"],w=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],I=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],k={keyword:w,literal:(W=>{const V=[];return W.forEach(re=>{V.push(re),re.toLowerCase()===re?V.push(re.toUpperCase()):V.push(re.toLowerCase())}),V})(y),built_in:I},D=W=>W.map(V=>V.replace(/\|\d+$/,"")),x={variants:[{match:[/new/,t.concat(f,"+"),t.concat("(?!",D(I).join("\\b|"),"\\b)"),a],scope:{1:"keyword",4:"title.class"}}]},q=t.concat(r,"\\b(?!\\()"),z={variants:[{match:[t.concat(/::/,t.lookahead(/(?!class\b)/)),q],scope:{2:"variable.constant"}},{match:[/::/,/class/],scope:{2:"variable.language"}},{match:[a,t.concat(/::/,t.lookahead(/(?!class\b)/)),q],scope:{1:"title.class",3:"variable.constant"}},{match:[a,t.concat("::",t.lookahead(/(?!class\b)/))],scope:{1:"title.class"}},{match:[a,/::/,/class/],scope:{1:"title.class",3:"variable.language"}}]},H={scope:"attr",match:t.concat(r,t.lookahead(":"),t.lookahead(/(?!::)/))},se={relevance:0,begin:/\(/,end:/\)/,keywords:k,contains:[H,o,z,e.C_BLOCK_COMMENT_MODE,N,_,x]},de={relevance:0,match:[/\b/,t.concat("(?!fn\\b|function\\b|",D(w).join("\\b|"),"|",D(I).join("\\b|"),"\\b)"),r,t.concat(f,"*"),t.lookahead(/(?=\()/)],scope:{3:"title.function.invoke"},contains:[se]};se.contains.push(de);const le=[H,z,e.C_BLOCK_COMMENT_MODE,N,_,x],ue={begin:t.concat(/#\[\s*\\?/,t.either(a,d)),beginScope:"meta",end:/]/,endScope:"meta",keywords:{literal:y,keyword:["new","array"]},contains:[{begin:/\[/,end:/]/,keywords:{literal:y,keyword:["new","array"]},contains:["self",...le]},...le,{scope:"meta",variants:[{match:a},{match:d}]}]};return{case_insensitive:!1,keywords:k,contains:[ue,e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/,keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE,contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},s,{scope:"variable.language",match:/\$this\b/},o,de,z,{match:[/const/,/\s/,r],scope:{1:"keyword",3:"variable.constant"}},x,{scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use"},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:k,contains:["self",ue,o,z,e.C_BLOCK_COMMENT_MODE,N,_]}]},{scope:"class",variants:[{beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait",illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/,contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{beginKeywords:"use",relevance:0,end:";",contains:[{match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},N,_]}}function rr(e){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}function ar(e){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}function or(e){const t=e.regex,i=new RegExp("[\\p{XID_Start}_]\\p{XID_Continue}*","u"),r=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],s={$pattern:/[A-Za-z]\w+|__\w+__/,keyword:r,built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"],literal:["__debug__","Ellipsis","False","None","NotImplemented","True"],type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"]},l={className:"meta",begin:/^(>>>|\.\.\.) /},c={className:"subst",begin:/\{/,end:/\}/,keywords:s,illegal:/#/},g={begin:/\{\{/,relevance:0},m={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,l],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,l],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,l,g,c]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,l,g,c]},{begin:/([uU]|[rR])'/,end:/'/,relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/,end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,g,c]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,g,c]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},p="[0-9](_?[0-9])*",f=`(\\b(${p}))?\\.(${p})|\\b(${p})\\.`,N=`\\b|${r.join("|")}`,_={className:"number",relevance:0,variants:[{begin:`(\\b(${p})|(${f}))[eE][+-]?(${p})[jJ]?(?=${N})`},{begin:`(${f})[jJ]?`},{begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${N})`},{begin:`\\b0[bB](_?[01])+[lL]?(?=${N})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${N})`},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${N})`},{begin:`\\b(${p})[jJ](?=${N})`}]},y={className:"comment",begin:t.lookahead(/# type:/),end:/$/,keywords:s,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},w={className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:["self",l,_,m,e.HASH_COMMENT_MODE]}]};return c.contains=[m,_,l],{name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:s,illegal:/(<\/|\?)|=>/,contains:[l,_,{scope:"variable.language",match:/\bself\b/},{beginKeywords:"if",relevance:0},{match:/\bor\b/,scope:"keyword"},m,y,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,i],scope:{1:"keyword",3:"title.function"},contains:[w]},{variants:[{match:[/\bclass/,/\s+/,i,/\s*/,/\(\s*/,i,/\s*\)/]},{match:[/\bclass/,/\s+/,i]}],scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[_,w,m]}]}}function sr(e){return{aliases:["pycon"],contains:[{className:"meta.prompt",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}function cr(e){const t=e.regex,i=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,r=t.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),a=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,d=t.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:i,keyword:"function if in break next repeat else for while",literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10",built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm"},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/,starts:{end:t.lookahead(t.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:i},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}]}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE],variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"',relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{1:"operator",2:"number"},match:[a,r]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,r]},{scope:{1:"punctuation",2:"number"},match:[d,r]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,r]}]},{scope:{3:"operator"},match:[i,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:a},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:d},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}}function lr(e){const t=e.regex,i="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",r=t.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),a=t.concat(r,/(::\w+)*/),o={"variable.constant":["__FILE__","__LINE__","__ENCODING__"],"variable.language":["self","super"],keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield",...["include","extend","prepend","public","private","protected","raise","throw"]],built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"],literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},l={begin:"#<",end:">"},c=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],g={className:"subst",begin:/#\{/,end:/\}/,keywords:o},m={className:"string",contains:[e.BACKSLASH_ESCAPE,g],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?,end:/>/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{begin:t.concat(/<<[-~]?'?/,t.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)),contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,g]})]}]},p="[1-9](_?[0-9])*|0",f="[0-9](_?[0-9])*",N={className:"number",relevance:0,variants:[{begin:`\\b(${p})(\\.(${f}))?([eE][+-]?(${f})|r)?i?\\b`},{begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{begin:"\\b0(_?[0-7])+r?i?\\b"}]},_={variants:[{match:/\(\)/},{className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0,keywords:o}]},x=[m,{variants:[{match:[/class\s+/,a,/\s+<\s+/,a]},{match:[/\b(class|module)\s+/,a]}],scope:{2:"title.class",4:"title.class.inherited"},keywords:o},{match:[/(include|extend)\s+/,a],scope:{2:"title.class"},keywords:o},{relevance:0,match:[a,/\.new[. (]/],scope:{1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},{relevance:0,match:r,scope:"title.class"},{match:[/def/,/\s+/,i],scope:{1:"keyword",3:"title.function"},contains:[_]},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[m,{begin:i}],relevance:0},N,{className:"variable",begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{className:"params",begin:/\|(?!=)/,end:/\|/,excludeBegin:!0,excludeEnd:!0,relevance:0,keywords:o},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,g],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(l,c),relevance:0}].concat(l,c);g.contains=x,_.contains=x;const se=[{begin:/^\s*=>/,starts:{end:"$",contains:x}},{className:"meta.prompt",begin:"^("+"[>?]>"+"|"+"[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]"+"|"+"(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>"+")(?=[ ])",starts:{end:"$",keywords:o,contains:x}}];return c.unshift(l),{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:o,illegal:/\/\*/,contains:[e.SHEBANG({binary:"ruby"})].concat(se).concat(c).concat(x)}}function dr(e){const t=e.regex,i=/(r#)?/,r=t.concat(i,e.UNDERSCORE_IDENT_RE),a=t.concat(i,e.IDENT_RE),d={className:"title.function.invoke",relevance:0,begin:t.concat(/\b/,/(?!let|for|while|if|else|match\b)/,a,t.lookahead(/\s*\(/))},o="([ui](8|16|32|64|128|size)|f(32|64))?",s=["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","union","unsafe","unsized","use","virtual","where","while","yield"],l=["true","false","Some","None","Ok","Err"],c=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],g=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"];return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:g,keyword:s,literal:l,built_in:c},illegal:"",contains:[e.C_LINE_COMMENT_MODE,e.COMMENT("/\\*","\\*/",{contains:["self"]}),e.inherit(e.QUOTE_STRING_MODE,{begin:/b?"/,illegal:null}),{className:"symbol",begin:/'[a-zA-Z_][a-zA-Z0-9_]*(?!')/},{scope:"string",variants:[{begin:/b?r(#*)"(.|\n)*?"\1(?!#)/},{begin:/b?'/,end:/'/,contains:[{scope:"char.escape",match:/\\('|\w|x\w{2}|u\w{4}|U\w{8})/}]}]},{className:"number",variants:[{begin:"\\b0b([01_]+)"+o},{begin:"\\b0o([0-7_]+)"+o},{begin:"\\b0x([A-Fa-f0-9_]+)"+o},{begin:"\\b(\\d[\\d_]*(\\.[0-9_]+)?([eE][+-]?[0-9_]+)?)"+o}],relevance:0},{begin:[/fn/,/\s+/,r],className:{1:"keyword",3:"title.function"}},{className:"meta",begin:"#!?\\[",end:"\\]",contains:[{className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE]}]},{begin:[/let/,/\s+/,/(?:mut\s+)?/,r],className:{1:"keyword",3:"keyword",4:"variable"}},{begin:[/for/,/\s+/,r,/\s+/,/in/],className:{1:"keyword",3:"variable",5:"keyword"}},{begin:[/type/,/\s+/,r],className:{1:"keyword",3:"title.class"}},{begin:[/(?:trait|enum|struct|union|impl|for)/,/\s+/,r],className:{1:"keyword",3:"title.class"}},{begin:e.IDENT_RE+"::",keywords:{keyword:"Self",built_in:c,type:g}},{className:"punctuation",begin:"->"},d]}}const ur=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),gr=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],pr=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],br=[...gr,...pr],mr=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),fr=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),_r=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),Er=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse();function hr(e){const t=ur(e),i=_r,r=fr,a="@[a-z-]+",d="and or not only",s={className:"variable",begin:"(\\$"+"[a-zA-Z-][a-zA-Z0-9_-]*"+")\\b",relevance:0};return{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t.CSS_NUMBER_MODE,{className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",begin:"\\b("+br.join("|")+")\\b",relevance:0},{className:"selector-pseudo",begin:":("+r.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+i.join("|")+")"},s,{begin:/\(/,end:/\)/,contains:[t.CSS_NUMBER_MODE]},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+Er.join("|")+")\\b"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:/:/,end:/[;}{]/,relevance:0,contains:[t.BLOCK_COMMENT,s,t.HEXCOLOR,t.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,t.IMPORTANT,t.FUNCTION_DISPATCH]},{begin:"@(page|font-face)",keywords:{$pattern:a,keyword:"@page @font-face"}},{begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:d,attribute:mr.join(" ")},contains:[{begin:a,className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"},s,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,t.HEXCOLOR,t.CSS_NUMBER_MODE]},t.FUNCTION_DISPATCH]}}function Nr(e){return{name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta.prompt",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}}function yr(e){const t=e.regex,i=e.COMMENT("--","$"),r={scope:"string",variants:[{begin:/'/,end:/'/,contains:[{match:/''/}]}]},a={begin:/"/,end:/"/,contains:[{match:/""/}]},d=["true","false","unknown"],o=["double precision","large object","with timezone","without timezone"],s=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],l=["add","asc","collation","desc","final","first","last","view"],c=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year"],g=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],m=["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"],p=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],f=g,N=[...c,...l].filter(D=>!g.includes(D)),_={scope:"variable",match:/@[a-z0-9][a-z0-9_]*/},y={scope:"operator",match:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0},w={match:t.concat(/\b/,t.either(...f),/\s*\(/),relevance:0,keywords:{built_in:f}};function I(D){return t.concat(/\b/,t.either(...D.map(x=>x.replace(/\s+/,"\\s+"))),/\b/)}const L={scope:"keyword",match:I(p),relevance:0};function k(D,{exceptions:x,when:q}={}){const z=q;return x=x||[],D.map(H=>H.match(/\|\d+$/)||x.includes(H)?H:z(H)?`${H}|0`:H)}return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/,keyword:k(N,{when:D=>D.length<3}),literal:d,type:s,built_in:m},contains:[{scope:"type",match:I(o)},L,w,_,r,a,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,i,y]}}function on(e){return e?typeof e=="string"?e:e.source:null}function Ce(e){return $("(?=",e,")")}function $(...e){return e.map(i=>on(i)).join("")}function Tr(e){const t=e[e.length-1];return typeof t=="object"&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function oe(...e){return"("+(Tr(e).capture?"":"?:")+e.map(r=>on(r)).join("|")+")"}const _t=e=>$(/\b/,e,/\w$/.test(e)?/\b/:/\B/),wr=["Protocol","Type"].map(_t),Kt=["init","self"].map(_t),Sr=["Any","Self"],ut=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","package","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ht=["false","nil","true"],vr=["assignment","associativity","higherThan","left","lowerThan","none","right"],Ar=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],qt=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],sn=oe(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),cn=oe(sn,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),gt=$(sn,cn,"*"),ln=oe(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),Ye=oe(ln,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),me=$(ln,Ye,"*"),We=$(/[A-Z]/,Ye,"*"),Or=["attached","autoclosure",$(/convention\(/,oe("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",$(/objc\(/,me,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Rr=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];function kr(e){const t={match:/\s+/,relevance:0},i=e.COMMENT("/\\*","\\*/",{contains:["self"]}),r=[e.C_LINE_COMMENT_MODE,i],a={match:[/\./,oe(...wr,...Kt)],className:{2:"keyword"}},d={match:$(/\./,oe(...ut)),relevance:0},o=ut.filter(F=>typeof F=="string").concat(["_|0"]),s=ut.filter(F=>typeof F!="string").concat(Sr).map(_t),l={variants:[{className:"keyword",match:oe(...s,...Kt)}]},c={$pattern:oe(/\b\w+/,/#\w+/),keyword:o.concat(Ar),literal:Ht},g=[a,d,l],m={match:$(/\./,oe(...qt)),relevance:0},p={className:"built_in",match:$(/\b/,oe(...qt),/(?=\()/)},f=[m,p],N={match:/->/,relevance:0},_={className:"operator",relevance:0,variants:[{match:gt},{match:`\\.(\\.|${cn})+`}]},y=[N,_],w="([0-9]_*)+",I="([0-9a-fA-F]_*)+",L={className:"number",relevance:0,variants:[{match:`\\b(${w})(\\.(${w}))?([eE][+-]?(${w}))?\\b`},{match:`\\b0x(${I})(\\.(${I}))?([pP][+-]?(${w}))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},k=(F="")=>({className:"subst",variants:[{match:$(/\\/,F,/[0\\tnr"']/)},{match:$(/\\/,F,/u\{[0-9a-fA-F]{1,8}\}/)}]}),D=(F="")=>({className:"subst",match:$(/\\/,F,/[\t ]*(?:[\r\n]|\r\n)/)}),x=(F="")=>({className:"subst",label:"interpol",begin:$(/\\/,F,/\(/),end:/\)/}),q=(F="")=>({begin:$(F,/"""/),end:$(/"""/,F),contains:[k(F),D(F),x(F)]}),z=(F="")=>({begin:$(F,/"/),end:$(/"/,F),contains:[k(F),x(F)]}),H={className:"string",variants:[q(),q("#"),q("##"),q("###"),z(),z("#"),z("##"),z("###")]},se=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}],de={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//,contains:se},le=F=>{const Ae=$(F,/\//),ye=$(/\//,F);return{begin:Ae,end:ye,contains:[...se,{scope:"comment",begin:`#(?!.*${ye})`,end:/$/}]}},ue={scope:"regexp",variants:[le("###"),le("##"),le("#"),de]},W={match:$(/`/,me,/`/)},V={className:"variable",match:/\$\d+/},re={className:"variable",match:`\\$${Ye}+`},fe=[W,V,re],P={match:/(@|#(un)?)available/,scope:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:Rr,contains:[...y,L,H]}]}},j={scope:"keyword",match:$(/@/,oe(...Or),Ce(oe(/\(/,/\s+/)))},ae={scope:"meta",match:$(/@/,me)},ne=[P,j,ae],ee={match:Ce(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:$(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Ye,"+")},{className:"type",match:We,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:$(/\s+&\s+/,Ce(We)),relevance:0}]},_e={begin:/,end:/>/,keywords:c,contains:[...r,...g,...ne,N,ee]};ee.contains.push(_e);const Ve={match:$(me,/\s*:/),keywords:"_|0",relevance:0},Le={begin:/\(/,end:/\)/,relevance:0,keywords:c,contains:["self",Ve,...r,ue,...g,...f,...y,L,H,...fe,...ne,ee]},Ie={begin:/,end:/>/,keywords:"repeat each",contains:[...r,ee]},Qe={begin:oe(Ce($(me,/\s*:/)),Ce($(me,/\s+/,me,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:me}]},De={begin:/\(/,end:/\)/,keywords:c,contains:[Qe,...r,...g,...y,L,H,...ne,ee,Le],endsParent:!0,illegal:/["']/},Et={match:[/(func|macro)/,/\s+/,oe(W.match,me,gt)],className:{1:"keyword",3:"title.function"},contains:[Ie,De,t],illegal:[/\[/,/%/]},ve={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[Ie,De,t],illegal:/\[|%/},Je={match:[/operator/,/\s+/,gt],className:{1:"keyword",3:"title"}},je={begin:[/precedencegroup/,/\s+/,We],className:{1:"keyword",3:"title"},contains:[ee],keywords:[...vr,...Ht],end:/}/},et={match:[/class\b/,/\s+/,/func\b/,/\s+/,/\b[A-Za-z_][A-Za-z0-9_]*\b/],scope:{1:"keyword",3:"keyword",5:"title.function"}},tt={match:[/class\b/,/\s+/,/var\b/],scope:{1:"keyword",3:"keyword"}},nt={begin:[/(struct|protocol|class|extension|enum|actor)/,/\s+/,me,/\s*/],beginScope:{1:"keyword",3:"title.class"},keywords:c,contains:[Ie,...g,{begin:/:/,end:/\{/,keywords:c,contains:[{scope:"title.class.inherited",match:We},...g],relevance:0}]};for(const F of H.variants){const Ae=F.contains.find(it=>it.label==="interpol");Ae.keywords=c;const ye=[...g,...f,...y,L,H,...fe];Ae.contains=[...ye,{begin:/\(/,end:/\)/,contains:["self",...ye]}]}return{name:"Swift",keywords:c,contains:[...r,Et,ve,et,tt,nt,Je,je,{beginKeywords:"import",end:/$/,contains:[...r],relevance:0},ue,...g,...f,...y,L,H,...fe,...ne,ee,Le]}}const Ze="[A-Za-z$_][0-9A-Za-z$_]*",dn=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],un=["true","false","null","undefined","NaN","Infinity"],gn=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],pn=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],bn=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],mn=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],fn=[].concat(bn,gn,pn);function Mr(e){const t=e.regex,i=(P,{after:j})=>{const ae=""+P[0].slice(1);return P.input.indexOf(ae,j)!==-1},r=Ze,a={begin:"<>",end:">"},d=/<[A-Za-z0-9\\._:-]+\s*\/>/,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(P,j)=>{const ae=P[0].length+P.index,ne=P.input[ae];if(ne==="<"||ne===","){j.ignoreMatch();return}ne===">"&&(i(P,{after:ae})||j.ignoreMatch());let ee;const _e=P.input.substring(ae);if(ee=_e.match(/^\s*=/)){j.ignoreMatch();return}if((ee=_e.match(/^\s+extends\s+/))&&ee.index===0){j.ignoreMatch();return}}},s={$pattern:Ze,keyword:dn,literal:un,built_in:fn,"variable.language":mn},l="[0-9](_?[0-9])*",c=`\\.(${l})`,g="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",m={className:"number",variants:[{begin:`(\\b(${g})((${c})|\\.)?|(${c}))[eE][+-]?(${l})\\b`},{begin:`\\b(${g})\\b((${c})\\b|\\.)?|(${c})\\b`},{begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{begin:"\\b0[0-7]+n?\\b"}],relevance:0},p={className:"subst",begin:"\\$\\{",end:"\\}",keywords:s,contains:[]},f={begin:".?html`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,p],subLanguage:"xml"}},N={begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,p],subLanguage:"css"}},_={begin:".?gql`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,p],subLanguage:"graphql"}},y={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,p]},I={className:"comment",variants:[e.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:"variable",begin:r+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},L=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,f,N,_,y,{match:/\$\d+/},m];p.contains=L.concat({begin:/\{/,end:/\}/,keywords:s,contains:["self"].concat(L)});const k=[].concat(I,p.contains),D=k.concat([{begin:/(\s*)\(/,end:/\)/,keywords:s,contains:["self"].concat(k)}]),x={className:"params",begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:D},q={variants:[{match:[/class/,/\s+/,r,/\s+/,/extends/,/\s+/,t.concat(r,"(",t.concat(/\./,r),")*")],scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{match:[/class/,/\s+/,r],scope:{1:"keyword",3:"title.class"}}]},z={relevance:0,match:t.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:"title.class",keywords:{_:[...gn,...pn]}},H={label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},se={variants:[{match:[/function/,/\s+/,r,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:"keyword",3:"title.function"},label:"func.def",contains:[x],illegal:/%/},de={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"};function le(P){return t.concat("(?!",P.join("|"),")")}const ue={match:t.concat(/\b/,le([...bn,"super","import"].map(P=>`${P}\\s*\\(`)),r,t.lookahead(/\s*\(/)),className:"title.function",relevance:0},W={begin:t.concat(/\./,t.lookahead(t.concat(r,/(?![0-9A-Za-z$_(])/))),end:r,excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},V={match:[/get|set/,/\s+/,r,/(?=\()/],className:{1:"keyword",3:"title.function"},contains:[{begin:/\(\)/},x]},re="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+e.UNDERSCORE_IDENT_RE+")\\s*=>",fe={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(re)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[x]};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:s,exports:{PARAMS_CONTAINS:D,CLASS_REFERENCE:z},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),H,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,f,N,_,y,I,{match:/\$\d+/},m,z,{scope:"attr",match:r+t.lookahead(":"),relevance:0},fe,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[I,e.REGEXP_MODE,{className:"function",begin:re,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:D}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:a.begin,end:a.end},{match:d},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,contains:["self"]}]}]},se,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[x,e.inherit(e.TITLE_MODE,{begin:r,className:"title.function"})]},{match:/\.\.\./,relevance:0},W,{match:"\\$"+r,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[x]},ue,de,q,V,{match:/\$[(.]/}]}}function Ir(e){const t=e.regex,i=Mr(e),r=Ze,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],d={begin:[/namespace/,/\s+/,e.IDENT_RE],beginScope:{1:"keyword",3:"title.class"}},o={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a},contains:[i.exports.CLASS_REFERENCE]},s={className:"meta",relevance:10,begin:/^\s*['"]use strict['"]/},l=["type","interface","public","private","protected","implements","declare","abstract","readonly","enum","override","satisfies"],c={$pattern:Ze,keyword:dn.concat(l),literal:un,built_in:fn.concat(a),"variable.language":mn},g={className:"meta",begin:"@"+r},m=(_,y,w)=>{const I=_.contains.findIndex(L=>L.label===y);if(I===-1)throw new Error("can not find mode to replace");_.contains.splice(I,1,w)};Object.assign(i.keywords,c),i.exports.PARAMS_CONTAINS.push(g);const p=i.contains.find(_=>_.scope==="attr"),f=Object.assign({},p,{match:t.concat(r,t.lookahead(/\s*\?:/))});i.exports.PARAMS_CONTAINS.push([i.exports.CLASS_REFERENCE,p,f]),i.contains=i.contains.concat([g,d,o,f]),m(i,"shebang",e.SHEBANG()),m(i,"use_strict",s);const N=i.contains.find(_=>_.label==="func.def");return N.relevance=0,Object.assign(i,{name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),i}function xr(e){const t=e.regex,i={className:"string",begin:/"(""|[^/n])"C\b/},r={className:"string",begin:/"/,end:/"/,illegal:/\n/,contains:[{begin:/""/}]},a=/\d{1,2}\/\d{1,2}\/\d{4}/,d=/\d{4}-\d{1,2}-\d{1,2}/,o=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,s=/\d{1,2}(:\d{1,2}){1,2}/,l={className:"literal",variants:[{begin:t.concat(/# */,t.either(d,a),/ *#/)},{begin:t.concat(/# */,s,/ *#/)},{begin:t.concat(/# */,o,/ *#/)},{begin:t.concat(/# */,t.either(d,a),/ +/,t.either(o,s),/ *#/)}]},c={className:"number",relevance:0,variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},g={className:"label",begin:/^\w+:/},m=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}]}),p=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield",built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort",type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort",literal:"true false nothing"},illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[i,r,l,c,g,m,p,{className:"meta",begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/,end:/$/,keywords:{keyword:"const disable else elseif enable end externalsource if region then"},contains:[p]}]}}function Cr(e){e.regex;const t=e.COMMENT(/\(;/,/;\)/);t.contains.push("self");const i=e.COMMENT(/;;/,/$/),r=["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"],a={begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword",3:"title.function"}},d={className:"variable",begin:/\$[\w_]+/},o={match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},s={className:"number",relevance:0,match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/},l={match:/(i32|i64|f32|f64)(?!\.)/,className:"type"},c={className:"keyword",match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/};return{name:"WebAssembly",keywords:{$pattern:/[\w.]+/,keyword:r},contains:[i,t,{match:[/(?:offset|align)/,/\s*/,/=/],className:{1:"keyword",3:"operator"}},d,o,a,e.QUOTE_STRING_MODE,l,c,s]}}function Lr(e){const t=e.regex,i=t.concat(/[\p{L}_]/u,t.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),r=/[\p{L}0-9._:-]+/u,a={className:"symbol",begin:/&[a-z]+;|[0-9]+;|[a-f0-9]+;/},d={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},o=e.inherit(d,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),c={endsWithParent:!0,illegal:/,relevance:0,contains:[{className:"attr",begin:r,relevance:0},{begin:/=\s*/,relevance:0,contains:[{className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[a]},{begin:/'/,end:/'/,contains:[a]},{begin:/[^\s"'=<>`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[d,l,s,o,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[d,o,l,s]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/
+
+
diff --git a/service/dist/images/bg-dark.webp b/service/dist/images/bg-dark.webp
new file mode 100644
index 0000000000000000000000000000000000000000..455c4e90b8462d30b1566135fd6e696f899d91df
GIT binary patch
literal 251202
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