diff --git a/.dockerignore b/.dockerignore
index 38d43ddb8..acd946f36 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,3 +1,50 @@
-*.md
-.idea
+# Git
.git
+.gitignore
+.gitattributes
+
+# IDE
+.idea
+.vscode
+*.swp
+*.swo
+
+# Documentation
+*.md
+docs/
+LICENSE
+
+# Development
+.venv
+__pycache__
+*.pyc
+*.pyo
+.pytest_cache
+.coverage
+htmlcov/
+
+# macOS
+.DS_Store
+._*
+
+# Frontend source (will be built from GitHub)
+fcb-fronted/
+themes/
+
+# Test files
+test_*.py
+tests/
+
+# Data (should be mounted as volume)
+data/
+
+# GitHub
+.github/
+
+# Docker
+Dockerfile
+docker-compose.yml
+.dockerignore
+
+# Claude
+.claude/
diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
index faa44beaf..a695ab6e6 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/docker-image.yml
@@ -5,6 +5,7 @@ on:
push:
branches:
- master
+ - dev
tags:
- 'v*'
@@ -31,6 +32,7 @@ jobs:
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
+ type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
type=raw,value=beta,enable={{is_default_branch}}
type=raw,value=latest,enable={{is_default_branch}}
diff --git a/.gitignore b/.gitignore
index 5fffab0a6..0cebc4ebe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -158,7 +158,8 @@ data/.env
# Ignore node_modules
node_modules/
-
AGENTS.md
-dist/
\ No newline at end of file
+dist/
+# Frontend themes (built from GitHub during Docker build)
+themes/
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index be2ece5ae..28453323e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,24 +1,56 @@
-FROM python:3.9.5-slim-buster
+# 第一阶段:构建前端主题
+FROM node:20-alpine AS frontend-builder
+
+RUN apk add --no-cache git python3 make g++
+
+WORKDIR /build
+
+# 克隆并构建 2024 主题
+RUN git clone --depth 1 https://github.com/vastsa/FileCodeBoxFronted.git /build/fronted-2024 && \
+ cd /build/fronted-2024 && \
+ npm install && \
+ npm run build
+
+# 克隆并构建 2023 主题
+RUN git clone --depth 1 https://github.com/vastsa/FileCodeBoxFronted2023.git /build/fronted-2023 && \
+ cd /build/fronted-2023 && \
+ npm install --legacy-peer-deps && \
+ npm run build
+
+# 第二阶段:构建最终镜像
+FROM python:3.12-slim-bookworm
LABEL author="Lan"
LABEL email="xzu@live.com"
-# 将当前目录下的文件复制到容器的 /app 目录
-COPY . /app
+WORKDIR /app
+
+# 复制项目文件(通过 .dockerignore 排除不必要的文件)
+COPY . .
-# 设置时区为亚洲/上海
-RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
+# 设置时区
+RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
+ echo 'Asia/Shanghai' > /etc/timezone
-# 设置工作目录
-WORKDIR /app
+# 从构建阶段复制编译好的前端主题
+COPY --from=frontend-builder /build/fronted-2024/dist ./themes/2024
+COPY --from=frontend-builder /build/fronted-2023/dist ./themes/2023
-# 删除不必要的目录,减少镜像体积
-RUN rm -rf docs fcb-fronted
+# 安装 Python 依赖
+RUN pip install --no-cache-dir -r requirements.txt
-# 安装依赖
-RUN pip install -r requirements.txt
+# 环境变量配置
+ENV HOST="0.0.0.0" \
+ PORT=12345 \
+ WORKERS=1 \
+ LOG_LEVEL="info"
-# 暴露端口
EXPOSE 12345
-# 启动应用
-CMD ["python", "main.py"]
\ No newline at end of file
+# 生产环境启动命令
+CMD uvicorn main:app \
+ --host $HOST \
+ --port $PORT \
+ --workers $WORKERS \
+ --log-level $LOG_LEVEL \
+ --proxy-headers \
+ --forwarded-allow-ips "*"
\ No newline at end of file
diff --git a/apps/admin/dependencies.py b/apps/admin/dependencies.py
index 8309b0794..205779bbb 100644
--- a/apps/admin/dependencies.py
+++ b/apps/admin/dependencies.py
@@ -50,7 +50,7 @@ def verify_token(token: str) -> dict:
).digest()
expected_signature_b64 = base64.b64encode(expected_signature).decode()
- if signature_b64 != expected_signature_b64:
+ if not hmac.compare_digest(signature_b64, expected_signature_b64):
raise ValueError("无效的签名")
# 解码payload
@@ -65,39 +65,41 @@ def verify_token(token: str) -> dict:
raise ValueError(f"token验证失败: {str(e)}")
+def _extract_bearer_token(authorization: str) -> str:
+ if not authorization or not authorization.startswith("Bearer "):
+ raise HTTPException(status_code=401, detail="未授权或授权校验失败")
+ token = authorization.split(" ", 1)[1].strip()
+ if not token:
+ raise HTTPException(status_code=401, detail="未授权或授权校验失败")
+ return token
+
+
+def _require_admin_payload(authorization: str) -> dict:
+ token = _extract_bearer_token(authorization)
+ try:
+ payload = verify_token(token)
+ except ValueError as e:
+ raise HTTPException(status_code=401, detail=str(e))
+ if not payload.get("is_admin", False):
+ raise HTTPException(status_code=401, detail="未授权或授权校验失败")
+ return payload
+
+
+ADMIN_PUBLIC_ENDPOINTS = {("POST", "/admin/login")}
+
+
async def admin_required(
authorization: str = Header(default=None), request: Request = None
):
"""
验证管理员权限
"""
- try:
- if not authorization or not authorization.startswith("Bearer "):
- is_admin = False
- else:
- try:
- token = authorization.split(" ")[1]
- payload = verify_token(token)
- is_admin = payload.get("is_admin", False)
- except ValueError as e:
- is_admin = False
-
- if request.url.path.startswith("/share/"):
- if not settings.openUpload and not is_admin:
- raise HTTPException(
- status_code=403, detail="本站未开启游客上传,如需上传请先登录后台"
- )
- else:
- if not is_admin:
- raise HTTPException(status_code=401, detail="未授权或授权校验失败")
- return is_admin
- except ValueError as e:
- raise HTTPException(status_code=401, detail=str(e))
+ if request and (request.method, request.url.path) in ADMIN_PUBLIC_ENDPOINTS:
+ return None
+ return _require_admin_payload(authorization)
-async def share_required_login(
- authorization: str = Header(default=None), request: Request = None
-):
+async def share_required_login(authorization: str = Header(default=None)):
"""
验证分享上传权限
@@ -109,21 +111,11 @@ async def share_required_login(
:return: 验证结果
"""
if not settings.openUpload:
- try:
- if not authorization or not authorization.startswith("Bearer "):
- raise HTTPException(
- status_code=403, detail="本站未开启游客上传,如需上传请先登录后台"
- )
-
- token = authorization.split(" ")[1]
- try:
- payload = verify_token(token)
- if not payload.get("is_admin", False):
- raise HTTPException(status_code=401, detail="未授权或授权校验失败")
- except ValueError as e:
- raise HTTPException(status_code=401, detail=str(e))
- except Exception as e:
- raise HTTPException(status_code=401, detail="认证失败:" + str(e))
+ if not authorization or not authorization.startswith("Bearer "):
+ raise HTTPException(
+ status_code=403, detail="本站未开启游客上传,如需上传请先登录后台"
+ )
+ _require_admin_payload(authorization)
return True
diff --git a/apps/admin/services.py b/apps/admin/services.py
index 015558c85..ba927e32f 100644
--- a/apps/admin/services.py
+++ b/apps/admin/services.py
@@ -4,7 +4,7 @@
from core.response import APIResponse
from core.storage import FileStorageInterface, storages
from core.settings import settings
-from apps.base.models import FileCodes, KeyValue
+from apps.base.models import FileCodes, KeyValue, file_codes_pydantic
from apps.base.utils import get_expire_info, get_file_path_name
from fastapi import HTTPException
from core.settings import data_root
@@ -26,7 +26,10 @@ async def list_files(self, page: int, size: int, keyword: str = ""):
await FileCodes.filter(prefix__icontains=keyword).limit(size).offset(offset)
)
total = await FileCodes.filter(prefix__icontains=keyword).count()
- return files, total
+ files_pydantic = [
+ await file_codes_pydantic.from_tortoise_orm(f) for f in files
+ ]
+ return files_pydantic, total
async def download_file(self, file_id: int):
file_code = await FileCodes.filter(id=file_id).first()
@@ -70,7 +73,7 @@ async def share_local_file(self, item):
class ConfigService:
def get_config(self):
- return settings.items()
+ return dict(settings.items())
async def update_config(self, data: dict):
admin_token = data.get("admin_token")
@@ -112,7 +115,12 @@ async def list_files(self):
if not os.path.exists(data_root / "local"):
os.makedirs(data_root / "local")
for file in os.listdir(data_root / "local"):
- files.append(LocalFileClass(file))
+ local_file = LocalFileClass(file)
+ files.append({
+ "file": local_file.file,
+ "ctime": local_file.ctime,
+ "size": local_file.size,
+ })
return files
async def delete_file(self, filename: str):
diff --git a/apps/admin/views.py b/apps/admin/views.py
index b5314539f..9501c16f1 100644
--- a/apps/admin/views.py
+++ b/apps/admin/views.py
@@ -19,7 +19,9 @@
from core.settings import settings
from core.utils import get_now, verify_password
-admin_api = APIRouter(prefix="/admin", tags=["管理"])
+admin_api = APIRouter(
+ prefix="/admin", tags=["管理"], dependencies=[Depends(admin_required)]
+)
@admin_api.post("/login")
@@ -32,7 +34,7 @@ async def login(data: LoginData):
@admin_api.get("/dashboard")
-async def dashboard(admin: bool = Depends(admin_required)):
+async def dashboard():
all_codes = await FileCodes.all()
all_size = str(sum([code.size for code in all_codes]))
sys_start = await KeyValue.filter(key="sys_start").first()
@@ -61,7 +63,6 @@ async def dashboard(admin: bool = Depends(admin_required)):
async def file_delete(
data: IDData,
file_service: FileService = Depends(get_file_service),
- admin: bool = Depends(admin_required),
):
await file_service.delete_file(data.id)
return APIResponse()
@@ -73,7 +74,6 @@ async def file_list(
size: int = 10,
keyword: str = "",
file_service: FileService = Depends(get_file_service),
- admin: bool = Depends(admin_required),
):
files, total = await file_service.list_files(page, size, keyword)
return APIResponse(
@@ -89,7 +89,6 @@ async def file_list(
@admin_api.get("/config/get")
async def get_config(
config_service: ConfigService = Depends(get_config_service),
- admin: bool = Depends(admin_required),
):
return APIResponse(detail=config_service.get_config())
@@ -98,7 +97,6 @@ async def get_config(
async def update_config(
data: dict,
config_service: ConfigService = Depends(get_config_service),
- admin: bool = Depends(admin_required),
):
data.pop("themesChoices")
await config_service.update_config(data)
@@ -109,7 +107,6 @@ async def update_config(
async def file_download(
id: int,
file_service: FileService = Depends(get_file_service),
- admin: bool = Depends(admin_required),
):
file_content = await file_service.download_file(id)
return file_content
@@ -118,7 +115,6 @@ async def file_download(
@admin_api.get("/local/lists")
async def get_local_lists(
local_file_service: LocalFileService = Depends(get_local_file_service),
- admin: bool = Depends(admin_required),
):
files = await local_file_service.list_files()
return APIResponse(detail=files)
@@ -128,7 +124,6 @@ async def get_local_lists(
async def delete_local_file(
item: DeleteItem,
local_file_service: LocalFileService = Depends(get_local_file_service),
- admin: bool = Depends(admin_required),
):
result = await local_file_service.delete_file(item.filename)
return APIResponse(detail=result)
@@ -138,7 +133,6 @@ async def delete_local_file(
async def share_local_file(
item: ShareItem,
file_service: FileService = Depends(get_file_service),
- admin: bool = Depends(admin_required),
):
share_info = await file_service.share_local_file(item)
return APIResponse(detail=share_info)
@@ -147,7 +141,6 @@ async def share_local_file(
@admin_api.patch("/file/update")
async def update_file(
data: UpdateFileData,
- admin: bool = Depends(admin_required),
):
file_code = await FileCodes.filter(id=data.id).first()
if not file_code:
diff --git a/apps/base/migrations/migrations_005.py b/apps/base/migrations/migrations_005.py
new file mode 100644
index 000000000..72c253d7f
--- /dev/null
+++ b/apps/base/migrations/migrations_005.py
@@ -0,0 +1,72 @@
+from tortoise import connections
+
+
+def _need_upgrade(columns: list[tuple]) -> bool:
+ for column in columns:
+ # PRAGMA table_info 返回 (cid, name, type, notnull, dflt_value, pk)
+ if column[1] == "size":
+ column_type = (column[2] or "").upper()
+ return "BIGINT" not in column_type
+ return False
+
+
+async def migrate():
+ conn = connections.get("default")
+ result = await conn.execute_query("PRAGMA table_info(filecodes)")
+ columns = result[1] if result and len(result) > 1 else []
+
+ if not columns or not _need_upgrade(columns):
+ return
+
+ await conn.execute_script(
+ """
+ BEGIN;
+ CREATE TABLE IF NOT EXISTS filecodes_new
+ (
+ id INTEGER not null
+ primary key autoincrement,
+ code VARCHAR(255) not null
+ unique,
+ prefix VARCHAR(255) default '' not null,
+ suffix VARCHAR(255) default '' not null,
+ uuid_file_name VARCHAR(255),
+ file_path VARCHAR(255),
+ size BIGINT default 0 not null,
+ text TEXT,
+ expired_at TIMESTAMP,
+ expired_count INT default 0 not null,
+ used_count INT default 0 not null,
+ created_at TIMESTAMP default CURRENT_TIMESTAMP not null,
+ file_hash VARCHAR(128),
+ is_chunked BOOL default False not null,
+ upload_id VARCHAR(128)
+ );
+
+ INSERT INTO filecodes_new (id, code, prefix, suffix, uuid_file_name, file_path, size, text,
+ expired_at, expired_count, used_count, created_at, file_hash,
+ is_chunked, upload_id)
+ SELECT id,
+ code,
+ prefix,
+ suffix,
+ uuid_file_name,
+ file_path,
+ size,
+ text,
+ expired_at,
+ expired_count,
+ used_count,
+ created_at,
+ file_hash,
+ is_chunked,
+ upload_id
+ FROM filecodes;
+
+ DROP TABLE filecodes;
+ ALTER TABLE filecodes_new
+ RENAME TO filecodes;
+ CREATE INDEX IF NOT EXISTS idx_filecodes_code_1c7ee7
+ on filecodes (code);
+ COMMIT;
+ """
+ )
diff --git a/apps/base/models.py b/apps/base/models.py
index 428a057f1..2d299c4e2 100644
--- a/apps/base/models.py
+++ b/apps/base/models.py
@@ -19,7 +19,7 @@ class FileCodes(models.Model):
suffix = fields.CharField(max_length=255, default="")
uuid_file_name = fields.CharField(max_length=255, null=True)
file_path = fields.CharField(max_length=255, null=True)
- size = fields.IntField(default=0)
+ size = fields.BigIntField(default=0)
text = fields.TextField(null=True)
expired_at = fields.DatetimeField(null=True)
expired_count = fields.IntField(default=0)
diff --git a/core/config.py b/core/config.py
new file mode 100644
index 000000000..a2dd3491c
--- /dev/null
+++ b/core/config.py
@@ -0,0 +1,21 @@
+from apps.base.models import KeyValue
+from apps.base.utils import ip_limit
+from core.settings import DEFAULT_CONFIG, settings
+
+
+async def ensure_settings_row() -> None:
+ await KeyValue.get_or_create(key="settings", defaults={"value": DEFAULT_CONFIG})
+
+
+def _sync_ip_limits() -> None:
+ ip_limit["error"].minutes = settings.errorMinute
+ ip_limit["error"].count = settings.errorCount
+ ip_limit["upload"].minutes = settings.uploadMinute
+ ip_limit["upload"].count = settings.uploadCount
+
+
+async def refresh_settings() -> None:
+ """从数据库读取最新配置并应用到运行时。"""
+ config_record = await KeyValue.filter(key="settings").first()
+ settings.user_config = config_record.value if config_record and config_record.value else {}
+ _sync_ip_limits()
diff --git a/core/database.py b/core/database.py
index 4421bf66e..1b041193a 100644
--- a/core/database.py
+++ b/core/database.py
@@ -1,6 +1,9 @@
+import asyncio
import glob
import importlib
import os
+from contextlib import asynccontextmanager
+from typing import IO
from tortoise import Tortoise
@@ -8,29 +11,91 @@
from core.settings import data_root
+_DB_FILE = os.path.join(data_root, "filecodebox.db")
+_STARTUP_LOCK_FILE = os.path.join(data_root, "filecodebox.startup.lock")
+
+
+def get_db_config() -> dict:
+ return {
+ "connections": {
+ "default": {
+ "engine": "tortoise.backends.sqlite",
+ "credentials": {
+ "file_path": _DB_FILE,
+ "journal_mode": "WAL",
+ "busy_timeout": 10000,
+ "foreign_keys": "ON",
+ },
+ }
+ },
+ "apps": {
+ "models": {
+ "models": ["apps.base.models"],
+ "default_connection": "default",
+ }
+ },
+ "use_tz": False,
+ "timezone": "Asia/Shanghai",
+ }
+
+
+def _lock_file(file_obj: IO[str]) -> None:
+ if os.name == "nt":
+ import msvcrt
+
+ # Windows 需要锁定至少 1 字节
+ if os.fstat(file_obj.fileno()).st_size == 0:
+ file_obj.write("0")
+ file_obj.flush()
+ msvcrt.locking(file_obj.fileno(), msvcrt.LK_LOCK, 1)
+ else:
+ import fcntl
+
+ fcntl.flock(file_obj.fileno(), fcntl.LOCK_EX)
+
+
+def _unlock_file(file_obj: IO[str]) -> None:
+ if os.name == "nt":
+ import msvcrt
+
+ msvcrt.locking(file_obj.fileno(), msvcrt.LK_UNLCK, 1)
+ else:
+ import fcntl
+
+ fcntl.flock(file_obj.fileno(), fcntl.LOCK_UN)
+
+
+@asynccontextmanager
+async def db_startup_lock():
+ os.makedirs(data_root, exist_ok=True)
+ lock_file = open(_STARTUP_LOCK_FILE, "a+", encoding="utf-8")
+ try:
+ await asyncio.to_thread(_lock_file, lock_file)
+ yield
+ finally:
+ await asyncio.to_thread(_unlock_file, lock_file)
+ lock_file.close()
+
+
async def init_db():
try:
- # 使用正确的Tortoise初始化配置格式
- db_config = {
- "db_url": f"sqlite://{data_root}/filecodebox.db",
- "modules": {"models": ["apps.base.models"]},
- "use_tz": False,
- "timezone": "Asia/Shanghai"
- }
-
- await Tortoise.init(**db_config)
-
- # 创建migrations表
- await Tortoise.get_connection("default").execute_script("""
- CREATE TABLE IF NOT EXISTS migrates (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- migration_file VARCHAR(255) NOT NULL UNIQUE,
- executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
- )
- """)
+ db_config = get_db_config()
+
+ if not Tortoise._inited:
+ await Tortoise.init(config=db_config)
+
+ async with db_startup_lock():
+ # 创建migrations表
+ await Tortoise.get_connection("default").execute_script("""
+ CREATE TABLE IF NOT EXISTS migrates (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ migration_file VARCHAR(255) NOT NULL UNIQUE,
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """)
- # 执行迁移
- await execute_migrations()
+ # 执行迁移
+ await execute_migrations()
except Exception as e:
logger.error(f"数据库初始化失败: {str(e)}")
diff --git a/core/response.py b/core/response.py
index 576911195..8afd49438 100644
--- a/core/response.py
+++ b/core/response.py
@@ -2,14 +2,14 @@
# @Author : Lan
# @File : response.py
# @Software: PyCharm
-from typing import Generic, TypeVar
+from typing import Generic, Optional, TypeVar
-from pydantic.v1.generics import GenericModel
+from pydantic import BaseModel
T = TypeVar("T")
-class APIResponse(GenericModel, Generic[T]):
+class APIResponse(BaseModel, Generic[T]):
code: int = 200
message: str = "ok"
- detail: T
+ detail: Optional[T] = None
diff --git a/core/settings.py b/core/settings.py
index 0b90a9db7..c8f5e4b44 100644
--- a/core/settings.py
+++ b/core/settings.py
@@ -65,7 +65,7 @@
],
"themesSelect": "themes/2024",
"errorMinute": 1,
- "errorCount": 1,
+ "errorCount": 10,
"serverWorkers": 1,
"serverHost": "0.0.0.0",
"serverPort": 12345,
diff --git a/core/storage.py b/core/storage.py
index 0e820bbc5..a40562676 100644
--- a/core/storage.py
+++ b/core/storage.py
@@ -4,6 +4,8 @@
# @Software: PyCharm
import base64
import hashlib
+import os
+import tempfile
from core.logger import logger
import shutil
from typing import Optional
@@ -14,7 +16,6 @@
import asyncio
from pathlib import Path
import datetime
-import io
import re
import aioboto3
from botocore.config import Config
@@ -23,7 +24,7 @@
from core.settings import data_root, settings
from apps.base.models import FileCodes, UploadChunk
from core.utils import get_file_url, sanitize_filename
-from fastapi.responses import FileResponse
+from fastapi.responses import FileResponse, StreamingResponse
class FileStorageInterface:
@@ -144,10 +145,20 @@ async def get_file_response(self, file_code: FileCodes):
filename = f"{file_code.prefix}{file_code.suffix}"
encoded_filename = quote(filename, safe='')
content_disposition = f"attachment; filename*=UTF-8''{encoded_filename}"
+
+ # 尝试获取文件系统大小,如果成功则设置 Content-Length
+ headers = {"Content-Disposition": content_disposition}
+ try:
+ content_length = file_path.stat().st_size
+ headers["Content-Length"] = str(content_length)
+ except Exception:
+ # 如果获取文件大小失败,则不提供 Content-Length
+ pass
+
return FileResponse(
file_path,
media_type="application/octet-stream",
- headers={"Content-Disposition": content_disposition},
+ headers=headers,
filename=filename # 保留原始文件名以备某些场景使用
)
@@ -275,11 +286,12 @@ async def save_file(self, file: UploadFile, save_path: str):
region_name=self.region_name,
config=Config(signature_version=self.signature_version),
) as s3:
- await s3.put_object(
- Bucket=self.bucket_name,
- Key=save_path,
- Body=await file.read(),
- ContentType=file.content_type,
+ # 使用 upload_fileobj 流式上传,避免将整个文件加载到内存
+ await s3.upload_fileobj(
+ file.file,
+ self.bucket_name,
+ save_path,
+ ExtraArgs={"ContentType": file.content_type or "application/octet-stream"},
)
async def delete_file(self, file_code: FileCodes):
@@ -296,12 +308,29 @@ async def delete_file(self, file_code: FileCodes):
async def get_file_response(self, file_code: FileCodes):
try:
filename = file_code.prefix + file_code.suffix
+ content_length = None # 初始化为 None,表示未知大小
+
async with self.session.client(
"s3",
endpoint_url=self.endpoint_url,
region_name=self.region_name,
config=Config(signature_version=self.signature_version),
) as s3:
+ # 尝试获取文件大小(HEAD请求)
+ try:
+ head_response = await s3.head_object(
+ Bucket=self.bucket_name,
+ Key=await file_code.get_file_path()
+ )
+ # 从HEAD响应中获取Content-Length
+ if 'ContentLength' in head_response:
+ content_length = head_response['ContentLength']
+ elif 'Content-Length' in head_response['ResponseMetadata']['HTTPHeaders']:
+ content_length = int(head_response['ResponseMetadata']['HTTPHeaders']['Content-Length'])
+ except Exception:
+ # 如果HEAD请求失败,则不提供 Content-Length
+ pass
+
link = await s3.generate_presigned_url(
"get_object",
Params={
@@ -310,20 +339,42 @@ async def get_file_response(self, file_code: FileCodes):
},
ExpiresIn=3600,
)
- tmp = io.BytesIO()
- async with aiohttp.ClientSession() as session:
- async with session.get(link) as resp:
- tmp.write(await resp.read())
- tmp.seek(0)
- content = tmp.read()
- tmp.close()
- return Response(
- content,
+
+ # 创建ClientSession并传递给生成器复用
+ session = aiohttp.ClientSession()
+
+ async def stream_generator():
+ try:
+ async with session.get(link) as resp:
+ if resp.status != 200:
+ raise HTTPException(
+ status_code=resp.status,
+ detail=f"从S3获取文件失败: {resp.status}"
+ )
+ # 设置块大小(例如64KB)
+ chunk_size = 65536
+ while True:
+ chunk = await resp.content.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+ finally:
+ await session.close()
+
+ from fastapi.responses import StreamingResponse
+ encoded_filename = quote(filename, safe='')
+ headers = {
+ "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"
+ }
+ if content_length is not None:
+ headers["Content-Length"] = str(content_length)
+ return StreamingResponse(
+ stream_generator(),
media_type="application/octet-stream",
- headers={
- "Content-Disposition": f'attachment; filename="{filename.encode("utf-8").decode("latin-1")}"'
- },
+ headers=headers
)
+ except HTTPException:
+ raise
except Exception:
raise HTTPException(status_code=503, detail="服务代理下载异常,请稍后再试")
@@ -376,12 +427,11 @@ async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes,
async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:
"""
合并 S3 上的分片文件
- 由于分片是独立对象存储的,需要下载后合并再上传
+ 使用 S3 的 multipart upload API 实现流式合并,避免内存问题
"""
file_sha256 = hashlib.sha256()
chunk_dir = str(Path(save_path).parent / "chunks" / upload_id)
- merged_data = io.BytesIO()
-
+
async with self.session.client(
's3',
endpoint_url=self.endpoint_url,
@@ -389,38 +439,70 @@ async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path:
region_name=self.region_name,
config=Config(signature_version=self.signature_version),
) as s3:
- # 按顺序读取并验证每个分片
- for i in range(chunk_info.total_chunks):
- chunk_key = f"{chunk_dir}/{i}.part"
- chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
- if not chunk_record:
- raise ValueError(f"分片{i}记录不存在")
-
- try:
- response = await s3.get_object(
- Bucket=self.bucket_name,
- Key=chunk_key
- )
- chunk_data = await response['Body'].read()
- except Exception as e:
- raise ValueError(f"分片{i}文件不存在: {e}")
-
- current_hash = hashlib.sha256(chunk_data).hexdigest()
- if current_hash != chunk_record.chunk_hash:
- raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
-
- file_sha256.update(chunk_data)
- merged_data.write(chunk_data)
-
- # 上传合并后的文件
- merged_data.seek(0)
- await s3.put_object(
+ # 创建 multipart upload
+ mpu = await s3.create_multipart_upload(
Bucket=self.bucket_name,
Key=save_path,
- Body=merged_data.getvalue(),
ContentType='application/octet-stream'
)
-
+ mpu_id = mpu['UploadId']
+ parts = []
+
+ try:
+ # 按顺序读取、验证并上传每个分片
+ for i in range(chunk_info.total_chunks):
+ chunk_key = f"{chunk_dir}/{i}.part"
+ chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
+ if not chunk_record:
+ raise ValueError(f"分片{i}记录不存在")
+
+ try:
+ response = await s3.get_object(
+ Bucket=self.bucket_name,
+ Key=chunk_key
+ )
+ chunk_data = await response['Body'].read()
+ except Exception as e:
+ raise ValueError(f"分片{i}文件不存在: {e}")
+
+ current_hash = hashlib.sha256(chunk_data).hexdigest()
+ if current_hash != chunk_record.chunk_hash:
+ raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
+
+ file_sha256.update(chunk_data)
+
+ # 上传分片到 multipart upload
+ part_response = await s3.upload_part(
+ Bucket=self.bucket_name,
+ Key=save_path,
+ UploadId=mpu_id,
+ PartNumber=i + 1, # S3 part numbers start at 1
+ Body=chunk_data
+ )
+ parts.append({
+ 'PartNumber': i + 1,
+ 'ETag': part_response['ETag']
+ })
+
+ # 释放内存
+ del chunk_data
+
+ # 完成 multipart upload
+ await s3.complete_multipart_upload(
+ Bucket=self.bucket_name,
+ Key=save_path,
+ UploadId=mpu_id,
+ MultipartUpload={'Parts': parts}
+ )
+ except Exception as e:
+ # 出错时取消 multipart upload
+ await s3.abort_multipart_upload(
+ Bucket=self.bucket_name,
+ Key=save_path,
+ UploadId=mpu_id
+ )
+ raise e
+
return save_path, file_sha256.hexdigest()
async def clean_chunks(self, upload_id: str, save_path: str):
@@ -602,20 +684,51 @@ async def get_file_response(self, file_code: FileCodes):
link = await asyncio.to_thread(
self._get_file_url, await file_code.get_file_path(), filename
)
- tmp = io.BytesIO()
- async with aiohttp.ClientSession() as session:
- async with session.get(link) as resp:
- tmp.write(await resp.read())
- tmp.seek(0)
- content = tmp.read()
- tmp.close()
- return Response(
- content,
+
+ content_length = None # 初始化为 None,表示未知大小
+
+ # 创建ClientSession并复用
+ session = aiohttp.ClientSession()
+
+ # 尝试发送HEAD请求获取Content-Length
+ try:
+ async with session.head(link) as resp:
+ if resp.status == 200 and 'Content-Length' in resp.headers:
+ content_length = int(resp.headers['Content-Length'])
+ except Exception:
+ # 如果HEAD请求失败,则不提供 Content-Length
+ pass
+
+ async def stream_generator():
+ try:
+ async with session.get(link) as resp:
+ if resp.status != 200:
+ raise HTTPException(
+ status_code=resp.status,
+ detail=f"从OneDrive获取文件失败: {resp.status}"
+ )
+ chunk_size = 65536
+ while True:
+ chunk = await resp.content.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+ finally:
+ await session.close()
+
+ encoded_filename = quote(filename, safe='')
+ headers = {
+ "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"
+ }
+ if content_length is not None:
+ headers["Content-Length"] = str(content_length)
+ return StreamingResponse(
+ stream_generator(),
media_type="application/octet-stream",
- headers={
- "Content-Disposition": f'attachment; filename="{filename.encode("utf-8").decode("latin-1")}"'
- },
+ headers=headers
)
+ except HTTPException:
+ raise
except Exception:
raise HTTPException(status_code=503, detail="服务代理下载异常,请稍后再试")
@@ -682,33 +795,44 @@ def _upload_merged(self, save_path: str, data: bytes):
current_folder.upload(filename, data).execute_query()
async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:
- """合并 OneDrive 上的分片文件"""
+ """合并 OneDrive 上的分片文件,使用临时文件避免内存问题"""
file_sha256 = hashlib.sha256()
chunk_dir = str(Path(save_path).parent / "chunks" / upload_id)
- merged_data = io.BytesIO()
-
- for i in range(chunk_info.total_chunks):
- chunk_path = f"{chunk_dir}/{i}.part"
- chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
- if not chunk_record:
- raise ValueError(f"分片{i}记录不存在")
-
- try:
- chunk_data = await asyncio.to_thread(self._read_chunk, chunk_path)
- except Exception as e:
- raise ValueError(f"分片{i}文件不存在: {e}")
-
- current_hash = hashlib.sha256(chunk_data).hexdigest()
- if current_hash != chunk_record.chunk_hash:
- raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
-
- file_sha256.update(chunk_data)
- merged_data.write(chunk_data)
-
- # 上传合并后的文件
- merged_data.seek(0)
- await asyncio.to_thread(self._upload_merged, save_path, merged_data.getvalue())
-
+
+ # 使用临时文件存储合并数据
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+ temp_path = temp_file.name
+
+ try:
+ async with aiofiles.open(temp_path, 'wb') as out_file:
+ for i in range(chunk_info.total_chunks):
+ chunk_path = f"{chunk_dir}/{i}.part"
+ chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
+ if not chunk_record:
+ raise ValueError(f"分片{i}记录不存在")
+
+ try:
+ chunk_data = await asyncio.to_thread(self._read_chunk, chunk_path)
+ except Exception as e:
+ raise ValueError(f"分片{i}文件不存在: {e}")
+
+ current_hash = hashlib.sha256(chunk_data).hexdigest()
+ if current_hash != chunk_record.chunk_hash:
+ raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
+
+ file_sha256.update(chunk_data)
+ await out_file.write(chunk_data)
+ del chunk_data # 释放内存
+
+ # 读取临时文件并上传
+ async with aiofiles.open(temp_path, 'rb') as f:
+ merged_content = await f.read()
+ await asyncio.to_thread(self._upload_merged, save_path, merged_content)
+ finally:
+ # 清理临时文件
+ if os.path.exists(temp_path):
+ os.unlink(temp_path)
+
return save_path, file_sha256.hexdigest()
def _delete_chunk_dir(self, chunk_dir: str):
@@ -765,7 +889,9 @@ def __init__(self):
)
async def save_file(self, file: UploadFile, save_path: str):
- await self.operator.write(save_path, file.file.read())
+ # 使用 asyncio.to_thread 避免阻塞事件循环
+ content = await asyncio.to_thread(file.file.read)
+ await self.operator.write(save_path, content)
async def delete_file(self, file_code: FileCodes):
await self.operator.delete(await file_code.get_file_path())
@@ -776,11 +902,54 @@ async def get_file_url(self, file_code: FileCodes):
async def get_file_response(self, file_code: FileCodes):
try:
filename = file_code.prefix + file_code.suffix
- content = await self.operator.read(await file_code.get_file_path())
+ content_length = None # 初始化为 None,表示未知大小
+
+ # 尝试获取文件大小
+ try:
+ stat_result = await self.operator.stat(await file_code.get_file_path())
+ if hasattr(stat_result, 'content_length') and stat_result.content_length:
+ content_length = stat_result.content_length
+ elif hasattr(stat_result, 'size') and stat_result.size:
+ content_length = stat_result.size
+ except Exception:
+ # 如果获取大小失败,则不提供 Content-Length
+ pass
+
+ # 尝试使用流式读取器
+ try:
+ # OpenDAL 可能提供 reader 方法返回一个异步读取器
+ reader = await self.operator.reader(await file_code.get_file_path())
+ except AttributeError:
+ # 如果 reader 方法不存在,回退到全量读取(兼容旧版本)
+ content = await self.operator.read(await file_code.get_file_path())
+ encoded_filename = quote(filename, safe='')
+ headers = {
+ "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"
+ }
+ if content_length is not None:
+ headers["Content-Length"] = str(content_length)
+ return Response(
+ content, headers=headers, media_type="application/octet-stream"
+ )
+
+ async def stream_generator():
+ chunk_size = 65536
+ while True:
+ chunk = await reader.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+
+ encoded_filename = quote(filename, safe='')
headers = {
- "Content-Disposition": f'attachment; filename="{filename}"'}
- return Response(
- content, headers=headers, media_type="application/octet-stream"
+ "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"
+ }
+ if content_length is not None:
+ headers["Content-Length"] = str(content_length)
+ return StreamingResponse(
+ stream_generator(),
+ media_type="application/octet-stream",
+ headers=headers
)
except Exception as e:
logger.info(e)
@@ -792,32 +961,43 @@ async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes,
await self.operator.write(chunk_path, chunk_data)
async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:
- """合并 OpenDAL 存储上的分片文件"""
+ """合并 OpenDAL 存储上的分片文件,使用临时文件避免内存问题"""
file_sha256 = hashlib.sha256()
chunk_dir = str(Path(save_path).parent / "chunks" / upload_id)
- merged_data = io.BytesIO()
-
- for i in range(chunk_info.total_chunks):
- chunk_path = f"{chunk_dir}/{i}.part"
- chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
- if not chunk_record:
- raise ValueError(f"分片{i}记录不存在")
-
- try:
- chunk_data = await self.operator.read(chunk_path)
- except Exception as e:
- raise ValueError(f"分片{i}文件不存在: {e}")
-
- current_hash = hashlib.sha256(chunk_data).hexdigest()
- if current_hash != chunk_record.chunk_hash:
- raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
-
- file_sha256.update(chunk_data)
- merged_data.write(chunk_data)
-
- # 写入合并后的文件
- merged_data.seek(0)
- await self.operator.write(save_path, merged_data.getvalue())
+
+ # 使用临时文件存储合并数据
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+ temp_path = temp_file.name
+
+ try:
+ async with aiofiles.open(temp_path, 'wb') as out_file:
+ for i in range(chunk_info.total_chunks):
+ chunk_path = f"{chunk_dir}/{i}.part"
+ chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
+ if not chunk_record:
+ raise ValueError(f"分片{i}记录不存在")
+
+ try:
+ chunk_data = await self.operator.read(chunk_path)
+ except Exception as e:
+ raise ValueError(f"分片{i}文件不存在: {e}")
+
+ current_hash = hashlib.sha256(chunk_data).hexdigest()
+ if current_hash != chunk_record.chunk_hash:
+ raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
+
+ file_sha256.update(chunk_data)
+ await out_file.write(chunk_data)
+ del chunk_data # 释放内存
+
+ # 读取临时文件并写入存储
+ async with aiofiles.open(temp_path, 'rb') as f:
+ merged_content = await f.read()
+ await self.operator.write(save_path, merged_content)
+ finally:
+ # 清理临时文件
+ if os.path.exists(temp_path):
+ os.unlink(temp_path)
return save_path, file_sha256.hexdigest()
@@ -910,7 +1090,7 @@ async def _delete_empty_dirs(self, file_path: str, session: aiohttp.ClientSessio
current_path = current_path.parent
async def save_file(self, file: UploadFile, save_path: str):
- """保存文件(自动创建目录)"""
+ """保存文件(自动创建目录,流式上传)"""
path_obj = Path(save_path)
directory_path = str(path_obj.parent)
# 提取原始文件名并进行清理
@@ -921,13 +1101,23 @@ async def save_file(self, file: UploadFile, save_path: str):
try:
# 先创建目录结构
await self._mkdir_p(directory_path)
- # 上传文件
+ # 上传文件(流式)
url = self._build_url(safe_save_path)
+
+ async def file_sender():
+ """流式读取文件内容"""
+ chunk_size = 256 * 1024 # 256KB chunks
+ while True:
+ chunk = await asyncio.to_thread(file.file.read, chunk_size)
+ if not chunk:
+ break
+ yield chunk
+
async with aiohttp.ClientSession(auth=self.auth) as session:
- content = await file.read()
async with session.put(
- url, data=content, headers={
- "Content-Type": file.content_type}
+ url,
+ data=file_sender(),
+ headers={"Content-Type": file.content_type or "application/octet-stream"}
) as resp:
if resp.status not in (200, 201, 204):
content = await resp.text()
@@ -969,26 +1159,50 @@ async def get_file_response(self, file_code: FileCodes):
try:
filename = file_code.prefix + file_code.suffix
url = self._build_url(await file_code.get_file_path())
- async with aiohttp.ClientSession(headers={
+ content_length = None # 初始化为 None,表示未知大小
+
+ # 创建ClientSession并复用(包含认证头)
+ session = aiohttp.ClientSession(headers={
"Authorization": f"Basic {base64.b64encode(f'{settings.webdav_username}:{settings.webdav_password}'.encode()).decode()}"
- }) as session:
- async with session.get(url) as resp:
- if resp.status != 200:
- raise HTTPException(
- status_code=resp.status,
- detail=f"文件获取失败{resp.status}: {await resp.text()}",
- )
- # 读取内容到内存
- content = await resp.read()
- return Response(
- content=content,
- media_type=resp.headers.get(
- "Content-Type", "application/octet-stream"
- ),
- headers={
- "Content-Disposition": f'attachment; filename="{filename.encode("utf-8").decode()}"'
- },
- )
+ })
+
+ # 尝试发送HEAD请求获取Content-Length
+ try:
+ async with session.head(url) as resp:
+ if resp.status == 200 and 'Content-Length' in resp.headers:
+ content_length = int(resp.headers['Content-Length'])
+ except Exception:
+ # 如果HEAD请求失败,则不提供 Content-Length
+ pass
+
+ async def stream_generator():
+ try:
+ async with session.get(url) as resp:
+ if resp.status != 200:
+ raise HTTPException(
+ status_code=resp.status,
+ detail=f"文件获取失败{resp.status}: {await resp.text()}",
+ )
+ chunk_size = 65536
+ while True:
+ chunk = await resp.content.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+ finally:
+ await session.close()
+
+ encoded_filename = quote(filename, safe='')
+ headers = {
+ "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"
+ }
+ if content_length is not None:
+ headers["Content-Length"] = str(content_length)
+ return StreamingResponse(
+ stream_generator(),
+ media_type="application/octet-stream",
+ headers=headers
+ )
except aiohttp.ClientError as e:
raise HTTPException(
status_code=503, detail=f"WebDAV连接异常: {str(e)}")
@@ -1014,52 +1228,70 @@ async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes,
async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:
"""
合并 WebDAV 上的分片文件
- 由于大多数 WebDAV 服务器不支持 PATCH 追加,这里下载所有分片后合并上传
+ 使用临时文件避免内存问题
"""
file_sha256 = hashlib.sha256()
chunk_dir = str(Path(save_path).parent / "chunks" / upload_id)
- merged_data = io.BytesIO()
-
- async with aiohttp.ClientSession(auth=self.auth) as session:
- # 按顺序读取并验证每个分片
- for i in range(chunk_info.total_chunks):
- chunk_path = f"{chunk_dir}/{i}.part"
- chunk_url = self._build_url(chunk_path)
-
- # 获取分片记录
- chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
- if not chunk_record:
- raise ValueError(f"分片{i}记录不存在")
-
- # 下载分片数据
- async with session.get(chunk_url) as resp:
- if resp.status != 200:
- raise ValueError(f"分片{i}文件不存在或无法访问")
- chunk_data = await resp.read()
-
- # 验证哈希
- current_hash = hashlib.sha256(chunk_data).hexdigest()
- if current_hash != chunk_record.chunk_hash:
- raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
-
- file_sha256.update(chunk_data)
- merged_data.write(chunk_data)
-
- # 确保目标目录存在
- output_dir = str(Path(save_path).parent)
- await self._mkdir_p(output_dir)
-
- # 上传合并后的文件
- output_url = self._build_url(save_path)
- merged_data.seek(0)
- async with session.put(output_url, data=merged_data.getvalue()) as resp:
- if resp.status not in (200, 201, 204):
- content = await resp.text()
- raise HTTPException(
- status_code=resp.status,
- detail=f"合并文件上传失败: {content[:200]}"
- )
-
+
+ # 使用临时文件存储合并数据,避免内存问题
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+ temp_path = temp_file.name
+
+ try:
+ async with aiohttp.ClientSession(auth=self.auth) as session:
+ # 按顺序读取并验证每个分片,写入临时文件
+ async with aiofiles.open(temp_path, 'wb') as out_file:
+ for i in range(chunk_info.total_chunks):
+ chunk_path = f"{chunk_dir}/{i}.part"
+ chunk_url = self._build_url(chunk_path)
+
+ # 获取分片记录
+ chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()
+ if not chunk_record:
+ raise ValueError(f"分片{i}记录不存在")
+
+ # 下载分片数据
+ async with session.get(chunk_url) as resp:
+ if resp.status != 200:
+ raise ValueError(f"分片{i}文件不存在或无法访问")
+ chunk_data = await resp.read()
+
+ # 验证哈希
+ current_hash = hashlib.sha256(chunk_data).hexdigest()
+ if current_hash != chunk_record.chunk_hash:
+ raise ValueError(f"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}")
+
+ file_sha256.update(chunk_data)
+ await out_file.write(chunk_data)
+ del chunk_data # 释放内存
+
+ # 确保目标目录存在
+ output_dir = str(Path(save_path).parent)
+ await self._mkdir_p(output_dir)
+
+ # 流式上传合并后的文件
+ output_url = self._build_url(save_path)
+
+ async def file_sender():
+ async with aiofiles.open(temp_path, 'rb') as f:
+ while True:
+ chunk = await f.read(256 * 1024)
+ if not chunk:
+ break
+ yield chunk
+
+ async with session.put(output_url, data=file_sender()) as resp:
+ if resp.status not in (200, 201, 204):
+ content = await resp.text()
+ raise HTTPException(
+ status_code=resp.status,
+ detail=f"合并文件上传失败: {content[:200]}"
+ )
+ finally:
+ # 清理临时文件
+ if os.path.exists(temp_path):
+ os.unlink(temp_path)
+
return save_path, file_sha256.hexdigest()
async def clean_chunks(self, upload_id: str, save_path: str):
diff --git a/core/tasks.py b/core/tasks.py
index c595ae2da..1347c0d33 100644
--- a/core/tasks.py
+++ b/core/tasks.py
@@ -11,15 +11,17 @@
from apps.base.models import FileCodes, UploadChunk
from apps.base.utils import ip_limit, get_chunk_file_path_name
+from core.config import refresh_settings
from core.settings import settings, data_root
from core.storage import FileStorageInterface, storages
from core.utils import get_now
async def delete_expire_files():
- file_storage: FileStorageInterface = storages[settings.file_storage]()
while True:
try:
+ await refresh_settings()
+ file_storage: FileStorageInterface = storages[settings.file_storage]()
# 遍历 share目录下的所有文件夹,删除空的文件夹,并判断父目录是否为空,如果为空也删除
if settings.file_storage == "local":
for root, dirs, files in os.walk(f"{data_root}/share/data"):
@@ -46,10 +48,11 @@ async def delete_expire_files():
async def clean_incomplete_uploads():
- file_storage: FileStorageInterface = storages[settings.file_storage]()
- expire_hours = getattr(settings, "chunk_expire_hours", 24)
while True:
try:
+ await refresh_settings()
+ file_storage: FileStorageInterface = storages[settings.file_storage]()
+ expire_hours = getattr(settings, "chunk_expire_hours", 24)
now = await get_now()
expire_time = now - datetime.timedelta(hours=expire_hours)
expired_sessions = await UploadChunk.filter(
diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md
index fecc86d43..b09d0698c 100644
--- a/docs/guide/getting-started.md
+++ b/docs/guide/getting-started.md
@@ -17,8 +17,61 @@ FileCodeBox 是一个简单高效的文件分享工具,支持文件临时中
### Docker 部署(推荐)
+#### 快速启动
+
+```bash
+docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:latest
+```
+
+#### Docker Compose
+
+```yml
+version: "3"
+services:
+ file-code-box:
+ image: lanol/filecodebox:latest
+ volumes:
+ - fcb-data:/app/data:rw
+ restart: unless-stopped
+ ports:
+ - "12345:12345"
+ environment:
+ - WORKERS=4
+ - LOG_LEVEL=info
+volumes:
+ fcb-data:
+ external: false
+```
+
+#### 环境变量
+
+| 变量 | 默认值 | 说明 |
+|------|--------|------|
+| `HOST` | `::` | 监听地址,支持 IPv4/IPv6 双栈 |
+| `PORT` | `12345` | 服务端口 |
+| `WORKERS` | `4` | 工作进程数,建议设置为 CPU 核心数 |
+| `LOG_LEVEL` | `info` | 日志级别:debug/info/warning/error |
+
+#### 自定义配置示例
+
```bash
-docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta
+docker run -d --restart=always \
+ -p 12345:12345 \
+ -v /opt/FileCodeBox/:/app/data \
+ -e WORKERS=8 \
+ -e LOG_LEVEL=warning \
+ --name filecodebox \
+ lanol/filecodebox:latest
+```
+
+### 配置反向代理(Nginx)
+
+```nginx
+location / {
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_pass http://localhost:12345;
+}
```
### 手动部署
diff --git a/readme_onedrive.md b/docs/guide/storage-onedrive.md
similarity index 100%
rename from readme_onedrive.md
rename to docs/guide/storage-onedrive.md
diff --git a/readme_opendal.md b/docs/guide/storage-opendal.md
similarity index 100%
rename from readme_opendal.md
rename to docs/guide/storage-opendal.md
diff --git a/main.py b/main.py
index 051bb4ab2..fee4d6c11 100644
--- a/main.py
+++ b/main.py
@@ -17,10 +17,11 @@
from apps.base.models import KeyValue
from apps.base.utils import ip_limit
from apps.base.views import share_api, chunk_api, presign_api
-from core.database import init_db
+from core.config import ensure_settings_row, refresh_settings
+from core.database import db_startup_lock, get_db_config, init_db
from core.logger import logger
from core.response import APIResponse
-from core.settings import data_root, settings, BASE_DIR, DEFAULT_CONFIG
+from core.settings import settings, BASE_DIR, DEFAULT_CONFIG
from core.tasks import delete_expire_files, clean_incomplete_uploads
from core.utils import hash_password, is_password_hashed
@@ -31,8 +32,9 @@ async def lifespan(app: FastAPI):
# 初始化数据库
await init_db()
- # 加载配置
- await load_config()
+ # 加载配置(多进程下串行化启动写操作)
+ async with db_startup_lock():
+ await load_config()
app.mount(
"/assets",
StaticFiles(directory=f"./{settings.themesSelect}/assets"),
@@ -57,13 +59,11 @@ async def lifespan(app: FastAPI):
async def load_config():
- user_config, _ = await KeyValue.get_or_create(
- key="settings", defaults={"value": DEFAULT_CONFIG}
- )
+ await ensure_settings_row()
await KeyValue.update_or_create(
key="sys_start", defaults={"value": int(time.time() * 1000)}
)
- settings.user_config = user_config.value
+ await refresh_settings()
await migrate_password_to_hash()
@@ -86,6 +86,11 @@ async def migrate_password_to_hash():
app = FastAPI(lifespan=lifespan)
+@app.middleware("http")
+async def refresh_settings_middleware(request, call_next):
+ await refresh_settings()
+ return await call_next(request)
+
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
@@ -97,15 +102,7 @@ async def migrate_password_to_hash():
# 使用 register_tortoise 来添加异常处理器
register_tortoise(
app,
- config={
- "connections": {"default": f"sqlite://{data_root}/filecodebox.db"},
- "apps": {
- "models": {
- "models": ["apps.base.models"],
- "default_connection": "default",
- },
- },
- },
+ config=get_db_config(),
generate_schemas=False,
add_exception_handlers=True,
)
diff --git a/readme.md b/readme.md
index ac411f65b..3733798f1 100644
--- a/readme.md
+++ b/readme.md
@@ -1,311 +1,311 @@
-# FileCodeBox - 文件快递柜
-
-
+# FileCodeBox
+
+### 文件快递柜 - 匿名口令分享文本和文件
+
+
-
匿名口令分享文本和文件,像拿快递一样取文件
+像拿快递一样取文件,无需注册,输入口令即可获取
-[](https://github.com/vastsa/FileCodeBox/stargazers)
-[](https://github.com/vastsa/FileCodeBox/network)
-[](https://github.com/vastsa/FileCodeBox/issues)
-[](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)
-[](https://qm.qq.com/q/PemPzhdEIM)
-[](https://www.python.org)
-[](https://fastapi.tiangolo.com)
-[](https://v3.vuejs.org)
+[](https://github.com/vastsa/FileCodeBox/stargazers)
+[](https://github.com/vastsa/FileCodeBox/network)
+[](https://github.com/vastsa/FileCodeBox/issues)
+[](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)
+[](https://hub.docker.com/r/lanol/filecodebox)
-[English](./readme_en.md) | [部署教程](https://github.com/vastsa/FileCodeBox/wiki/部署教程) | [常见问题](https://github.com/vastsa/FileCodeBox/wiki/常见问题)
+[](https://www.python.org)
+[](https://fastapi.tiangolo.com)
+[](https://vuejs.org)
+
+[English](./readme_en.md) | [在线演示](https://share.lanol.cn) | [部署教程](https://github.com/vastsa/FileCodeBox/wiki/部署教程) | [常见问题](https://github.com/vastsa/FileCodeBox/wiki/常见问题) | [QQ群: 739673698](https://qm.qq.com/q/PemPzhdEIM)
+
+```bash
+# 🚀 一键部署
+docker run -d -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest
+# 国内镜像(如果上面拉取缓慢): docker.cnb.cool/aixk/filecodebox
+```
-## 🚀 更新计划
+---
-- [ ] 2025年皮肤
-- [ ] 文件收集功能
+## 目录
-## 📝 项目简介
+- [项目简介](#-项目简介)
+- [功能特性](#-功能特性)
+- [界面预览](#-界面预览)
+- [快速开始](#-快速开始)
+- [使用指南](#-使用指南)
+- [开发指南](#-开发指南)
+- [常见问题](#-常见问题)
+- [贡献指南](#-贡献指南)
+- [项目统计](#-项目统计)
+- [免责声明](#-免责声明)
-FileCodeBox 是一个基于 FastAPI + Vue3 开发的轻量级文件分享工具。它允许用户通过简单的方式分享文本和文件,接收者只需要一个提取码就可以取得文件,就像从快递柜取出快递一样简单。
+---
-## 🖼️ 功能预览
+## 📝 项目简介
-别问前端源码怎么是js了,麻烦仔细看下面的内容
-
+FileCodeBox 是一个轻量级的文件分享工具,基于 **FastAPI + Vue3** 开发。用户可以通过简单的方式匿名分享文本和文件,接收者只需输入提取码即可获取内容——就像从快递柜取出快递一样简单。
-### 新版界面
+### 应用场景
-
+| 场景 | 描述 |
+|------|------|
+| 📁 **临时文件分享** | 快速分享文件,无需注册登录 |
+| 📝 **代码片段分享** | 分享代码、配置文件等文本内容 |
+| 🕶️ **匿名文件传输** | 保护隐私的点对点传输 |
+| 🔄 **跨设备传输** | 在不同设备间快速同步文件 |
+| 💾 **临时存储** | 支持自定义过期时间的云存储 |
+| 🌐 **私有服务** | 搭建企业或个人专属分享服务 |
-### 经典界面
+---
+
+## ✨ 功能特性
-
+
-## 🎯 应用场景
+### 🚀 轻量高效
+- FastAPI + SQLite3 后端
+- Vue3 + Element Plus 前端
+- Docker 一键部署
+- 资源占用极低
-
-
-
-📁 临时文件分享
-快速分享单个文件,无需注册登录
-
-📝 文本快速分享
-分享代码片段、文本内容等
+
+
+### 🔒 安全可靠
+- IP 上传频率限制
+- 提取码错误次数限制
+- 文件自动过期清理
+- 支持管理员认证
+
-
-🕶️ 匿名文件传输
-保护隐私的文件传输方式
+
+
+### 📤 便捷上传
+- 拖拽上传
+- 复制粘贴上传
+- 命令行 curl 上传
+- 批量文件上传
+
-
-💾 临时文件存储
-支持设置过期时间的文件存储
+
+
+### 🎫 灵活分享
+- 随机/自定义提取码
+- 可设置有效期(时间/次数)
+- 支持永久有效
+- 文本和文件统一管理
+
-
-🔄 跨平台传输
-在不同设备间快速传输文件
+
+
+### 💾 多存储支持
+- 本地文件系统
+- S3 兼容存储
+- [OneDrive](./docs/guide/storage-onedrive.md)
+- [OpenDAL](./docs/guide/storage-opendal.md)
+
-
-🌐 小型分享服务
-搭建私有的文件分享服务
+
+
+### 🌍 国际化
+- 简体中文
+- 繁体中文
+- English
+- 响应式设计 / 深色模式
+
-## ✨ 核心特性
+---
+
+## 🖼️ 界面预览
+> 前端源码仓库:[2024主题](https://github.com/vastsa/FileCodeBoxFronted) | [2023主题](https://github.com/vastsa/FileCodeBoxFronted2023)
+
+
+🎨 新版界面 (2024)
+
+
+
+
+
+📦 经典界面 (2023)
+
+
+
+
+---
## 🚀 快速开始
-### Docker 部署
+### Docker 部署(推荐)
-#### Docker CLI
+**方式一:Docker CLI**
```bash
-docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta
+# Docker Hub(推荐)
+docker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest
+
+# 国内镜像(如果 Docker Hub 拉取缓慢)
+docker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox docker.cnb.cool/aixk/filecodebox
```
-#### Docker Compose
+**方式二:Docker Compose**
-```yml
-version: "3"
+```yaml
services:
- file-code-box:
+ filecodebox:
image: lanol/filecodebox:latest
- volumes:
- - fcb-data:/app/data:rw
+ container_name: filecodebox
restart: unless-stopped
ports:
- "12345:12345"
-volumes:
- fcb-data:
- external: false
+ volumes:
+ - ./data:/app/data
+ environment:
+ - WORKERS=4
+ - LOG_LEVEL=info
+```
+
+```bash
+docker compose up -d
```
-### 配置反向代理(Nginx示例)
+**环境变量说明**
+
+| 变量 | 默认值 | 说明 |
+|------|--------|------|
+| `HOST` | `::` | 监听地址(支持 IPv4/IPv6 双栈) |
+| `PORT` | `12345` | 服务端口 |
+| `WORKERS` | `4` | 工作进程数(建议设为 CPU 核心数) |
+| `LOG_LEVEL` | `info` | 日志级别:`debug` / `info` / `warning` / `error` |
+
+### 反向代理配置
-请注意,必须添加以下配置来确保正确处理客户端IP和代理请求:
+使用 Nginx 时,请添加以下配置以正确获取客户端 IP:
```nginx
location / {
- proxy_set_header X-Real-IP $remote_addr; # 设置真实客户端IP
+ proxy_pass http://127.0.0.1:12345;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_pass http://localhost:12345;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ client_max_body_size 100m; # 根据需要调整上传大小限制
}
```
### 手动部署
-1. 克隆项目
-
```bash
+# 1. 克隆项目
git clone https://github.com/vastsa/FileCodeBox.git
-```
-
-2. 安装依赖
-
-```bash
cd FileCodeBox
-pip install -r requirements.txt
-```
-3. 启动服务
+# 2. 安装依赖
+pip install -r requirements.txt
-```bash
+# 3. 启动服务
python main.py
```
-## 📖 使用说明
-
-### 分享文件
-
-1. 打开网页,点击"分享文件"
-2. 选择或拖拽文件
-3. 设置过期时间和次数
-4. 获取提取码
+---
-### 获取文件
+## 📖 使用指南
-1. 打开网页,输入提取码
-2. 点击获取
-3. 下载文件或查看文本
+### 基础操作
-### 管理面板
+| 操作 | 步骤 |
+|------|------|
+| **分享文件** | 打开网页 → 选择/拖拽文件 → 设置有效期 → 获取提取码 |
+| **获取文件** | 打开网页 → 输入提取码 → 下载文件或查看文本 |
+| **管理后台** | 访问 `/#/admin` → 输入密码 `FileCodeBox2023` |
-1. 访问 `/#/admin`
-2. 输入管理员密码 `FileCodeBox2023`
-3. 管理文件和配置
+### 命令行使用(curl)
-### 命令行上传(curl)
+
+点击展开 curl 使用示例
-支持通过 curl 命令上传文件并获取取件码:
+**上传文件**
```bash
-# 上传文件(默认1天有效期)
+# 基础上传(默认 1 天有效期)
curl -X POST "http://localhost:12345/share/file/" \
- -F "file=@/path/to/your/file.txt"
+ -F "file=@/path/to/file.txt"
-# 上传文件并指定有效期(1小时)
+# 指定 1 小时有效期
curl -X POST "http://localhost:12345/share/file/" \
- -F "file=@/path/to/your/file.txt" \
+ -F "file=@/path/to/file.txt" \
-F "expire_value=1" \
-F "expire_style=hour"
-# 上传文件并指定有效期(可下载10次)
+# 指定下载 10 次后过期
curl -X POST "http://localhost:12345/share/file/" \
- -F "file=@/path/to/your/file.txt" \
+ -F "file=@/path/to/file.txt" \
-F "expire_value=10" \
-F "expire_style=count"
+```
+
+**分享文本**
-# 分享文本
+```bash
curl -X POST "http://localhost:12345/share/text/" \
- -F "text=这是要分享的文本内容"
+ -F "text=要分享的文本内容"
+```
-# 通过取件码下载文件
-curl -L "http://localhost:12345/share/select/?code=取件码" -o downloaded_file
+**下载文件**
+
+```bash
+curl -L "http://localhost:12345/share/select/?code=提取码" -o filename
```
-**参数说明:**
-- `expire_value`: 有效期数值(默认1)
-- `expire_style`: 有效期类型
- - `day` - 天数
- - `hour` - 小时
- - `minute` - 分钟
- - `count` - 下载次数
- - `forever` - 永久有效
+**有效期参数**
+
+| `expire_style` | 说明 |
+|----------------|------|
+| `day` | 天数 |
+| `hour` | 小时 |
+| `minute` | 分钟 |
+| `count` | 下载次数 |
+| `forever` | 永久有效 |
+
+**返回示例**
-**返回示例:**
```json
{
"code": 200,
@@ -317,101 +317,155 @@ curl -L "http://localhost:12345/share/select/?code=取件码" -o downloaded_file
}
```
-> 注意:如果管理面板关闭了游客上传(`openUpload=false`),需要先登录获取 token,然后在请求中添加 `Authorization: Bearer ` 头。
-
-**需要认证时的用法:**
+**需要认证时**(管理员关闭游客上传后)
```bash
-# 1. 先登录获取 token
+# 1. 获取 token
curl -X POST "http://localhost:12345/admin/login" \
-H "Content-Type: application/json" \
-d '{"password": "FileCodeBox2023"}'
-# 返回示例:
-# {"code":200,"msg":"success","detail":{"token":"xxx.xxx.xxx","token_type":"Bearer"}}
-
-# 2. 使用 token 上传文件
+# 2. 携带 token 上传
curl -X POST "http://localhost:12345/share/file/" \
- -H "Authorization: Bearer xxx.xxx.xxx" \
- -F "file=@/path/to/your/file.txt"
-
-# 2. 使用 token 分享文本
-curl -X POST "http://localhost:12345/share/text/" \
- -H "Authorization: Bearer xxx.xxx.xxx" \
- -F "text=这是要分享的文本内容"
+ -H "Authorization: Bearer " \
+ -F "file=@/path/to/file.txt"
```
+
+
+---
+
## 🛠 开发指南
### 项目结构
```
FileCodeBox/
-├── apps/ # 应用代码
-│ ├── admin/ # 管理后台
-│ └── base/ # 基础功能
-├── core/ # 核心功能
-├── data/ # 数据目录
-└── fcb-fronted/ # 前端代码
+├── apps/ # 应用模块
+│ ├── admin/ # 管理后台
+│ └── base/ # 基础功能
+├── core/ # 核心模块
+├── data/ # 数据目录(运行时生成)
+├── docs/ # 文档
+└── main.py # 入口文件
```
-### 开发环境
-
-- Python 3.8+
-- Node.js 14+
-- Vue 3
-- FastAPI
-
### 本地开发
-1. 后端开发
+**后端**
```bash
+pip install -r requirements.txt
python main.py
```
-2. 前端开发
+**前端**
```bash
+# 前端仓库: https://github.com/vastsa/FileCodeBoxFronted
cd fcb-fronted
npm install
npm run dev
```
-## 🤝 贡献指南
+### 技术栈
+
+| 类别 | 技术 |
+|------|------|
+| **后端框架** | FastAPI 0.128+ / Uvicorn |
+| **数据库** | SQLite + Tortoise ORM |
+| **数据验证** | Pydantic 2.x |
+| **异步支持** | aiofiles / aiohttp / aioboto3 |
+| **对象存储** | S3 协议 / OneDrive / OpenDAL |
+| **前端框架** | Vue 3 + Element Plus + Vite |
+| **运行环境** | Python 3.8+ / Node.js 18+ |
+| **容器化** | Docker / Docker Compose |
-1. Fork 本项目
-2. 创建新分支 `git checkout -b feature/xxx`
-3. 提交更改 `git commit -m 'Add xxx'`
-4. 推送到分支 `git push origin feature/xxx`
-5. 提交 Pull Request
+---
## ❓ 常见问题
-### Q: 如何修改上传大小限制?
+
+如何修改上传大小限制?
+
+在管理面板中修改 `uploadSize` 配置项。如果使用 Nginx 反向代理,还需修改 `client_max_body_size`。
+
+
+
+如何配置存储引擎?
-A: 在管理面板中修改配置项 `uploadSize`
+在管理面板中选择存储引擎类型并配置相应参数。支持本地存储、S3、OneDrive、OpenDAL 等。
+
-### Q: 如何配置存储引擎?
+
+如何备份数据?
-A: 在管理面板中选择存储引擎并配置相应参数
+备份 `data` 目录即可,包含数据库和上传的文件。
+
-### Q: 如何备份数据?
+
+如何修改管理员密码?
-A: 备份 `data` 目录即可
+登录管理面板后,在系统设置中修改 `adminPassword` 配置项。
+
-更多问题请访问 [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题)
+更多问题请访问 [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题) 或加入 [QQ群: 739673698](https://qm.qq.com/q/PemPzhdEIM)
+
+---
+
+## 🤝 贡献指南
+
+欢迎提交 Issue 和 Pull Request!
+
+```bash
+# 1. Fork 并克隆
+git clone https://github.com/your-username/FileCodeBox.git
+
+# 2. 创建分支
+git checkout -b feature/your-feature
+
+# 3. 提交更改
+git commit -m "feat: add your feature"
+
+# 4. 推送并创建 PR
+git push origin feature/your-feature
+```
+
+---
## 📊 项目统计
-
+
+
+
+

[](https://star-history.com/#vastsa/FileCodeBox&Date)
+
+---
+
+## 🗓 更新计划
+
+- [ ] 2025 年新皮肤
+- [ ] 文件收集功能
+
+---
+
## 📜 免责声明
-本项目开源仅供学习使用,不得用于任何违法用途,否则后果自负,与作者无关。使用时请保留项目地址和版权信息。
+本项目开源仅供学习交流使用,不得用于任何违法用途,否则后果自负,与作者无关。使用本项目时请保留项目地址和版权信息。
+
+---
+
+
+
+**如果觉得项目不错,欢迎 ⭐ Star 支持!**
+
+Made with ❤️ by [vastsa](https://github.com/vastsa)
+
+
diff --git a/readme_en.md b/readme_en.md
index 35c0c0e60..6448f23ac 100644
--- a/readme_en.md
+++ b/readme_en.md
@@ -1,307 +1,311 @@
+
+
# FileCodeBox
-
+### Anonymous File & Text Sharing with Passcode
-
+
-
Share text and files anonymously with a passcode, like picking up a package
+Share files like picking up a package — no registration required, just enter the passcode
-[](https://github.com/vastsa/FileCodeBox/stargazers)
-[](https://github.com/vastsa/FileCodeBox/network)
-[](https://github.com/vastsa/FileCodeBox/issues)
-[](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)
-[](https://qm.qq.com/q/PemPzhdEIM)
-[](https://www.python.org)
-[](https://fastapi.tiangolo.com)
-[](https://v3.vuejs.org)
+[](https://github.com/vastsa/FileCodeBox/stargazers)
+[](https://github.com/vastsa/FileCodeBox/network)
+[](https://github.com/vastsa/FileCodeBox/issues)
+[](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)
+[](https://hub.docker.com/r/lanol/filecodebox)
-[简体中文](./readme.md) | [Deployment Guide](https://github.com/vastsa/FileCodeBox/wiki/Deployment-Guide) | [FAQ](https://github.com/vastsa/FileCodeBox/wiki/FAQ)
+[](https://www.python.org)
+[](https://fastapi.tiangolo.com)
+[](https://vuejs.org)
+
+[简体中文](./README.md) | [Live Demo](https://share.lanol.cn) | [Documentation](https://github.com/vastsa/FileCodeBox/wiki/Deployment-Guide) | [FAQ](https://github.com/vastsa/FileCodeBox/wiki/FAQ)
+
+```bash
+# 🚀 Quick Deploy
+docker run -d -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest
+# China Mirror (if slow): docker.cnb.cool/aixk/filecodebox
+```
-## 📝 Introduction
+---
-FileCodeBox is a lightweight file sharing tool developed with FastAPI + Vue3. It allows users to share text and files
-easily, where recipients only need a passcode to retrieve the files, just like picking up a package from a delivery
-locker.
+## Table of Contents
-## 🖼️ Preview
+- [Introduction](#-introduction)
+- [Features](#-features)
+- [Screenshots](#-screenshots)
+- [Quick Start](#-quick-start)
+- [Usage Guide](#-usage-guide)
+- [Development](#-development)
+- [FAQ](#-faq)
+- [Contributing](#-contributing)
+- [Statistics](#-statistics)
+- [Disclaimer](#-disclaimer)
-
+---
-### New Interface
+## 📝 Introduction
-
+FileCodeBox is a lightweight file sharing tool built with **FastAPI + Vue3**. Users can anonymously share text and files, and recipients only need to enter a passcode to retrieve the content — just like picking up a package from a locker.
-### Classic Interface
+### Use Cases
-
+| Scenario | Description |
+|----------|-------------|
+| 📁 **Temporary File Sharing** | Quick file sharing without registration |
+| 📝 **Code Snippet Sharing** | Share code, config files, and text content |
+| 🕶️ **Anonymous Transfer** | Privacy-protected peer-to-peer transfer |
+| 🔄 **Cross-Device Transfer** | Quickly sync files between devices |
+| 💾 **Temporary Storage** | Cloud storage with custom expiration |
+| 🌐 **Private Service** | Build your own enterprise or personal sharing service |
-## 🎯 Use Cases
+---
+
+## ✨ Features
-
-📁 Temporary File Sharing
-Quick file sharing without registration
+
+
+### 🚀 Lightweight & Fast
+- FastAPI + SQLite3 backend
+- Vue3 + Element Plus frontend
+- One-click Docker deployment
+- Minimal resource usage
+
-
-📝 Quick Text Sharing
-Share code snippets and text content
+
+
+### 🔒 Secure & Reliable
+- IP upload rate limiting
+- Passcode attempt limiting
+- Auto file expiration cleanup
+- Admin authentication support
+
-
-🕶️ Anonymous Transfer
-Privacy-protected file transfer
+
+
+### 📤 Easy Upload
+- Drag & drop upload
+- Copy & paste upload
+- Command line curl upload
+- Batch file upload
+
-
-💾 Temporary Storage
-File storage with expiration time
+
+
+### 🎫 Flexible Sharing
+- Random / custom passcodes
+- Set expiration (time/count)
+- Permanent validity support
+- Unified text & file management
+
-
-🔄 Cross-platform Transfer
-Quick file transfer between devices
+
+
+### 💾 Multiple Storage
+- Local file system
+- S3-compatible storage
+- [OneDrive](./docs/guide/storage-onedrive.md)
+- [OpenDAL](./docs/guide/storage-opendal.md)
+
-
-🌐 Private Share Service
-Build your own file sharing service
+
+
+### 🌍 Internationalization
+- Simplified Chinese
+- Traditional Chinese
+- English
+- Responsive design / Dark mode
+
-## ✨ Core Features
+---
+
+## 🖼️ Screenshots
+> Frontend repositories: [2024 Theme](https://github.com/vastsa/FileCodeBoxFronted) | [2023 Theme](https://github.com/vastsa/FileCodeBoxFronted2023)
+
+
+🎨 New Interface (2024)
+
+
+
+
+
+📦 Classic Interface (2023)
+
+
+
+
+---
## 🚀 Quick Start
-### Docker Deployment
+### Docker Deployment (Recommended)
-#### Docker CLI
+**Option 1: Docker CLI**
```bash
-docker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta
+# Docker Hub (Recommended)
+docker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest
+
+# China Mirror (if Docker Hub is slow)
+docker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox docker.cnb.cool/aixk/filecodebox
```
-#### Docker Compose
+**Option 2: Docker Compose**
-```ymlfix
-version: "3"
-services:s
- file-code-box:
+```yaml
+services:
+ filecodebox:
image: lanol/filecodebox:latest
- volumes:
- - fcb-data:/app/data:rw
+ container_name: filecodebox
restart: unless-stopped
ports:
- "12345:12345"
-volumes:
- fcb-data:
- external: false
+ volumes:
+ - ./data:/app/data
+ environment:
+ - WORKERS=4
+ - LOG_LEVEL=info
+```
+
+```bash
+docker compose up -d
```
-### Configure reverse proxy (Nginx example)
+**Environment Variables**
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `HOST` | `::` | Listen address (supports IPv4/IPv6 dual-stack) |
+| `PORT` | `12345` | Service port |
+| `WORKERS` | `4` | Worker processes (recommended: CPU cores) |
+| `LOG_LEVEL` | `info` | Log level: `debug` / `info` / `warning` / `error` |
+
+### Reverse Proxy Configuration
-Please note that the following configurations must be added to ensure proper handling of client IP and proxy requests:
+When using Nginx, add the following configuration to properly obtain client IP:
```nginx
location / {
-proxy_set_header X-Real-IP $remote_addr; #Set real client IP
-proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-proxy_pass http://localhost:12345 ;
+ proxy_pass http://127.0.0.1:12345;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ client_max_body_size 100m; # Adjust upload size limit as needed
}
```
### Manual Deployment
-1. Clone the repository
-
```bash
+# 1. Clone the repository
git clone https://github.com/vastsa/FileCodeBox.git
-```
-
-2. Install dependencies
-
-```bash
cd FileCodeBox
-pip install -r requirements.txt
-```
-3. Start the service
+# 2. Install dependencies
+pip install -r requirements.txt
-```bash
+# 3. Start the service
python main.py
```
-## 📖 Usage Guide
-
-### Share Files
+---
-1. Open the website, click "Share File"
-2. Select or drag files
-3. Set expiration time and count
-4. Get the passcode
-
-### Retrieve Files
+## 📖 Usage Guide
-1. Open the website, enter passcode
-2. Click retrieve
-3. Download file or view text
+### Basic Operations
-### Admin Panel
+| Operation | Steps |
+|-----------|-------|
+| **Share File** | Open website → Select/drag files → Set expiration → Get passcode |
+| **Retrieve File** | Open website → Enter passcode → Download file or view text |
+| **Admin Panel** | Visit `/#/admin` → Enter password `FileCodeBox2023` |
-1. Visit `/#/admin`
-2. Enter admin password `FileCodeBox2023`
-3. Manage files and settings
+### Command Line Usage (curl)
-### Command Line Upload (curl)
+
+Click to expand curl examples
-Upload files and get extraction codes via curl:
+**Upload File**
```bash
-# Upload file (default 1 day expiration)
+# Basic upload (default 1 day expiration)
curl -X POST "http://localhost:12345/share/file/" \
- -F "file=@/path/to/your/file.txt"
+ -F "file=@/path/to/file.txt"
-# Upload file with expiration (1 hour)
+# Set 1 hour expiration
curl -X POST "http://localhost:12345/share/file/" \
- -F "file=@/path/to/your/file.txt" \
+ -F "file=@/path/to/file.txt" \
-F "expire_value=1" \
-F "expire_style=hour"
-# Upload file with download limit (10 downloads)
+# Set expiration after 10 downloads
curl -X POST "http://localhost:12345/share/file/" \
- -F "file=@/path/to/your/file.txt" \
+ -F "file=@/path/to/file.txt" \
-F "expire_value=10" \
-F "expire_style=count"
+```
+
+**Share Text**
-# Share text
+```bash
curl -X POST "http://localhost:12345/share/text/" \
- -F "text=This is the text content to share"
+ -F "text=Text content to share"
+```
+
+**Download File**
-# Download file by extraction code
-curl -L "http://localhost:12345/share/select/?code=YOUR_CODE" -o downloaded_file
+```bash
+curl -L "http://localhost:12345/share/select/?code=PASSCODE" -o filename
```
-**Parameters:**
-- `expire_value`: Expiration value (default 1)
-- `expire_style`: Expiration type
- - `day` - Days
- - `hour` - Hours
- - `minute` - Minutes
- - `count` - Download count
- - `forever` - Never expire
+**Expiration Parameters**
+
+| `expire_style` | Description |
+|----------------|-------------|
+| `day` | Days |
+| `hour` | Hours |
+| `minute` | Minutes |
+| `count` | Download count |
+| `forever` | Never expire |
+
+**Response Example**
-**Response Example:**
```json
{
"code": 200,
@@ -313,102 +317,155 @@ curl -L "http://localhost:12345/share/select/?code=YOUR_CODE" -o downloaded_file
}
```
-> Note: If guest upload is disabled in admin panel (`openUpload=false`), you need to login first to get a token, then add `Authorization: Bearer ` header to your requests.
-
-**When Authentication Required:**
+**When Authentication Required** (after admin disables guest upload)
```bash
-# 1. Login to get token
+# 1. Get token
curl -X POST "http://localhost:12345/admin/login" \
-H "Content-Type: application/json" \
-d '{"password": "FileCodeBox2023"}'
-# Response:
-# {"code":200,"msg":"success","detail":{"token":"xxx.xxx.xxx","token_type":"Bearer"}}
-
-# 2. Upload file with token
+# 2. Upload with token
curl -X POST "http://localhost:12345/share/file/" \
- -H "Authorization: Bearer xxx.xxx.xxx" \
- -F "file=@/path/to/your/file.txt"
-
-# 3. Share text with token
-curl -X POST "http://localhost:12345/share/text/" \
- -H "Authorization: Bearer xxx.xxx.xxx" \
- -F "text=This is the text content to share"
+ -H "Authorization: Bearer " \
+ -F "file=@/path/to/file.txt"
```
-## 🛠 Development Guide
+
+
+---
+
+## 🛠 Development
### Project Structure
```
FileCodeBox/
-├── apps/ # Application code
-│ ├── admin/ # Admin backend
-│ └── base/ # Base functions
-├── core/ # Core functions
-├── data/ # Data directory
-└── fcb-fronted/ # Frontend code
+├── apps/ # Application modules
+│ ├── admin/ # Admin backend
+│ └── base/ # Base functionality
+├── core/ # Core modules
+├── data/ # Data directory (generated at runtime)
+├── docs/ # Documentation
+└── main.py # Entry point
```
-### Development Environment
-
-- Python 3.8+
-- Node.js 14+
-- Vue 3
-- FastAPI
-
### Local Development
-1. Backend development
+**Backend**
```bash
+pip install -r requirements.txt
python main.py
```
-2. Frontend development
+**Frontend**
```bash
+# Frontend repo: https://github.com/vastsa/FileCodeBoxFronted
cd fcb-fronted
npm install
npm run dev
```
-## 🤝 Contributing
+### Tech Stack
-1. Fork the project
-2. Create your feature branch `git checkout -b feature/xxx`
-3. Commit your changes `git commit -m 'Add xxx'`
-4. Push to the branch `git push origin feature/xxx`
-5. Open a Pull Request
+| Category | Technology |
+|----------|------------|
+| **Backend Framework** | FastAPI 0.128+ / Uvicorn |
+| **Database** | SQLite + Tortoise ORM |
+| **Data Validation** | Pydantic 2.x |
+| **Async Support** | aiofiles / aiohttp / aioboto3 |
+| **Object Storage** | S3 Protocol / OneDrive / OpenDAL |
+| **Frontend Framework** | Vue 3 + Element Plus + Vite |
+| **Runtime** | Python 3.8+ / Node.js 18+ |
+| **Containerization** | Docker / Docker Compose |
+
+---
## ❓ FAQ
-### Q: How to modify upload size limit?
+
+How to modify upload size limit?
+
+Modify the `uploadSize` configuration in the admin panel. If using Nginx reverse proxy, also modify `client_max_body_size`.
+
-A: Change `uploadSize` in admin panel
+
+How to configure storage engine?
-### Q: How to configure storage engine?
+Select the storage engine type and configure parameters in the admin panel. Supports local storage, S3, OneDrive, OpenDAL, etc.
+
-A: Select storage engine and configure parameters in admin panel
+
+How to backup data?
-### Q: How to backup data?
+Backup the `data` directory, which contains the database and uploaded files.
+
-A: Backup the `data` directory
+
+How to change admin password?
-For more questions, visit [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题)
+After logging into the admin panel, modify the `adminPassword` configuration in system settings.
+
-## �� Project Statistics and Analytics
+For more questions, visit [Wiki](https://github.com/vastsa/FileCodeBox/wiki/FAQ)
+
+---
+
+## 🤝 Contributing
+
+Issues and Pull Requests are welcome!
+
+```bash
+# 1. Fork and clone
+git clone https://github.com/your-username/FileCodeBox.git
+
+# 2. Create branch
+git checkout -b feature/your-feature
+
+# 3. Commit changes
+git commit -m "feat: add your feature"
+
+# 4. Push and create PR
+git push origin feature/your-feature
+```
+
+---
+
+## 📊 Statistics
-
+
+
+
+

[](https://star-history.com/#vastsa/FileCodeBox&Date)
+
+---
+
+## 🗓 Roadmap
+
+- [ ] 2025 New Theme
+- [ ] File Collection Feature
+
+---
+
## 📜 Disclaimer
-This project is open-source for learning purposes only. It should not be used for any illegal purposes. The author is
-not responsible for any consequences. Please retain the project address and copyright information when using it.
\ No newline at end of file
+This project is open-source for learning and communication purposes only. It should not be used for any illegal purposes. The author is not responsible for any consequences. Please retain the project address and copyright information when using it.
+
+---
+
+
+
+**If you find this project helpful, please give it a ⭐ Star!**
+
+Made with ❤️ by [vastsa](https://github.com/vastsa)
+
+
diff --git a/requirements.txt b/requirements.txt
index 3a87944ca..d5d62a87c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,8 @@
-aioboto3==13.4.0
+aioboto3==15.5.0
aiohttp==3.13.3
-aiofiles==24.1.0
-fastapi==0.115.6
-pydantic==2.10.4
-uvicorn==0.34.0
-tortoise-orm==0.22.1
-python-multipart==0.0.20
+aiofiles==25.1.0
+fastapi==0.128.0
+pydantic==2.12.5
+uvicorn==0.40.0
+tortoise-orm==0.25.3
+python-multipart==0.0.21
diff --git a/themes/2023/assets/AboutView-DYjyUJF_.js b/themes/2023/assets/AboutView-DYjyUJF_.js
deleted file mode 100644
index ae739c8fd..000000000
--- a/themes/2023/assets/AboutView-DYjyUJF_.js
+++ /dev/null
@@ -1 +0,0 @@
-import{a as s,a8 as n,P as c,N as e,U as r,R as a,j as o,o as i}from"./index-CxMsK_Ni.js";const l={style:{"text-align":"center"}},u={style:{color:"#333"},href:"https://github.com/vastsa/FileCodeBox"},f=s({__name:"AboutView",setup(_){const{t}=n();return(m,p)=>(i(),c("main",l,[e("span",null,[r(a(o(t)("admin.about.source1"))+" ",1),e("em",null,[e("a",u,a(o(t)("admin.about.source2")),1)])])]))}});export{f as default};
diff --git a/themes/2023/assets/AdminView-BC_-34Xk.css b/themes/2023/assets/AdminView-BC_-34Xk.css
deleted file mode 100644
index b73547e37..000000000
--- a/themes/2023/assets/AdminView-BC_-34Xk.css
+++ /dev/null
@@ -1 +0,0 @@
-.el-container{box-sizing:border-box;display:flex;flex:1;flex-basis:auto;flex-direction:row;min-width:0}.el-container.is-vertical{flex-direction:column}.el-aside{box-sizing:border-box;flex-shrink:0;overflow:auto;width:var(--el-aside-width,300px)}.el-footer{--el-footer-padding:0 20px;--el-footer-height:60px;box-sizing:border-box;flex-shrink:0;height:var(--el-footer-height);padding:var(--el-footer-padding)}.el-header{--el-header-padding:0 20px;--el-header-height:60px;box-sizing:border-box;flex-shrink:0;height:var(--el-header-height);padding:var(--el-header-padding)}.el-main{--el-main-padding:20px;box-sizing:border-box;display:block;flex:1;flex-basis:auto;overflow:auto;padding:var(--el-main-padding)}.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transform:scaleY(1);transform-origin:center top;transition:var(--el-transition-md-fade)}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{transform-origin:center bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transform:scaleY(1);transform-origin:center bottom;transition:var(--el-transition-md-fade)}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transform:scale(1);transform-origin:top left;transition:var(--el-transition-md-fade)}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.el-collapse-transition-enter-active,.el-collapse-transition-leave-active{transition:var(--el-transition-duration) max-height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter-from,.el-list-leave-to{opacity:0;transform:translateY(-30px)}.el-list-leave-active{position:absolute!important}.el-opacity-transition{transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}:root{--el-menu-active-color:var(--el-color-primary);--el-menu-text-color:var(--el-text-color-primary);--el-menu-hover-text-color:var(--el-color-primary);--el-menu-bg-color:var(--el-fill-color-blank);--el-menu-hover-bg-color:var(--el-color-primary-light-9);--el-menu-item-height:56px;--el-menu-sub-item-height:calc(var(--el-menu-item-height) - 6px);--el-menu-horizontal-height:60px;--el-menu-horizontal-sub-item-height:36px;--el-menu-item-font-size:var(--el-font-size-base);--el-menu-item-hover-fill:var(--el-color-primary-light-9);--el-menu-border-color:var(--el-border-color);--el-menu-base-level-padding:20px;--el-menu-level-padding:20px;--el-menu-icon-width:24px}.el-menu{background-color:var(--el-menu-bg-color);border-right:1px solid var(--el-menu-border-color);box-sizing:border-box;list-style:none;margin:0;padding-left:0;position:relative}.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item,.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item-group__title,.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-sub-menu__title{padding-left:calc(var(--el-menu-base-level-padding) + var(--el-menu-level)*var(--el-menu-level-padding));white-space:nowrap}.el-menu:not(.el-menu--collapse) .el-sub-menu__title{padding-right:calc(var(--el-menu-base-level-padding) + var(--el-menu-icon-width))}.el-menu--horizontal{border-right:none;display:flex;flex-wrap:nowrap;height:var(--el-menu-horizontal-height)}.el-menu--horizontal.el-menu--popup-container{height:unset}.el-menu--horizontal.el-menu{border-bottom:1px solid var(--el-menu-border-color)}.el-menu--horizontal>.el-menu-item{align-items:center;border-bottom:2px solid transparent;color:var(--el-menu-text-color);display:inline-flex;height:100%;justify-content:center;margin:0}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-sub-menu:focus,.el-menu--horizontal>.el-sub-menu:hover{outline:none}.el-menu--horizontal>.el-sub-menu:hover .el-sub-menu__title{color:var(--el-menu-hover-text-color)}.el-menu--horizontal>.el-sub-menu.is-active .el-sub-menu__title{border-bottom:2px solid var(--el-menu-active-color);color:var(--el-menu-active-color)}.el-menu--horizontal>.el-sub-menu .el-sub-menu__title{border-bottom:2px solid transparent;color:var(--el-menu-text-color);height:100%}.el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover{background-color:var(--el-menu-bg-color)}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-sub-menu__title{align-items:center;background-color:var(--el-menu-bg-color);color:var(--el-menu-text-color);display:flex;height:var(--el-menu-horizontal-sub-item-height);line-height:var(--el-menu-horizontal-sub-item-height);padding:0 10px}.el-menu--horizontal .el-menu .el-sub-menu__title{padding-right:40px}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-sub-menu.is-active>.el-sub-menu__title{color:var(--el-menu-active-color)}.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,.el-menu--horizontal .el-menu-item:not(.is-disabled):hover{background-color:var(--el-menu-hover-bg-color);color:var(--el-menu-hover-text-color);outline:none}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid var(--el-menu-active-color);color:var(--el-menu-active-color)!important}.el-menu--collapse{width:calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding)*2)}.el-menu--collapse>.el-menu-item [class^=el-icon],.el-menu--collapse>.el-menu-item-group>ul>.el-sub-menu>.el-sub-menu__title [class^=el-icon],.el-menu--collapse>.el-sub-menu>.el-sub-menu__title [class^=el-icon]{margin:0;text-align:center;vertical-align:middle;width:var(--el-menu-icon-width)}.el-menu--collapse>.el-menu-item .el-sub-menu__icon-arrow,.el-menu--collapse>.el-menu-item-group>ul>.el-sub-menu>.el-sub-menu__title .el-sub-menu__icon-arrow,.el-menu--collapse>.el-sub-menu>.el-sub-menu__title .el-sub-menu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item-group>ul>.el-sub-menu>.el-sub-menu__title>span,.el-menu--collapse>.el-menu-item>span,.el-menu--collapse>.el-sub-menu>.el-sub-menu__title>span{display:inline-block;height:0;overflow:hidden;visibility:hidden;width:0}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-menu .el-sub-menu{min-width:200px}.el-menu--collapse .el-sub-menu.is-active .el-sub-menu__title{color:var(--el-menu-active-color)}.el-menu--popup{border:none;border-radius:var(--el-border-radius-small);box-shadow:var(--el-box-shadow-light);min-width:200px;padding:5px 0;z-index:100}.el-menu .el-icon{flex-shrink:0}.el-menu-item{align-items:center;box-sizing:border-box;color:var(--el-menu-text-color);cursor:pointer;display:flex;font-size:var(--el-menu-item-font-size);height:var(--el-menu-item-height);line-height:var(--el-menu-item-height);list-style:none;padding:0 var(--el-menu-base-level-padding);position:relative;transition:border-color var(--el-transition-duration),background-color var(--el-transition-duration),color var(--el-transition-duration);white-space:nowrap}.el-menu-item *{vertical-align:bottom}.el-menu-item i{color:inherit}.el-menu-item:focus,.el-menu-item:hover{outline:none}.el-menu-item:hover{background-color:var(--el-menu-hover-bg-color)}.el-menu-item.is-disabled{background:none!important;cursor:not-allowed;opacity:.25}.el-menu-item [class^=el-icon]{font-size:18px;margin-right:5px;text-align:center;vertical-align:middle;width:var(--el-menu-icon-width)}.el-menu-item.is-active{color:var(--el-menu-active-color)}.el-menu-item.is-active i{color:inherit}.el-menu-item .el-menu-tooltip__trigger{align-items:center;box-sizing:border-box;display:inline-flex;height:100%;left:0;padding:0 var(--el-menu-base-level-padding);position:absolute;top:0;width:100%}.el-sub-menu{list-style:none;margin:0;padding-left:0}.el-sub-menu__title{align-items:center;box-sizing:border-box;color:var(--el-menu-text-color);cursor:pointer;display:flex;font-size:var(--el-menu-item-font-size);height:var(--el-menu-item-height);line-height:var(--el-menu-item-height);list-style:none;padding:0 var(--el-menu-base-level-padding);position:relative;transition:border-color var(--el-transition-duration),background-color var(--el-transition-duration),color var(--el-transition-duration);white-space:nowrap}.el-sub-menu__title *{vertical-align:bottom}.el-sub-menu__title i{color:inherit}.el-sub-menu__title:focus,.el-sub-menu__title:hover{outline:none}.el-sub-menu__title.is-disabled{background:none!important;cursor:not-allowed;opacity:.25}.el-sub-menu__title:hover{background-color:var(--el-menu-hover-bg-color)}.el-sub-menu .el-menu{border:none}.el-sub-menu .el-menu-item{height:var(--el-menu-sub-item-height);line-height:var(--el-menu-sub-item-height)}.el-sub-menu__hide-arrow .el-sub-menu__icon-arrow{display:none!important}.el-sub-menu.is-active .el-sub-menu__title{border-bottom-color:var(--el-menu-active-color)}.el-sub-menu.is-disabled .el-menu-item,.el-sub-menu.is-disabled .el-sub-menu__title{background:none!important;cursor:not-allowed;opacity:.25}.el-sub-menu .el-icon{font-size:18px;margin-right:5px;text-align:center;vertical-align:middle;width:var(--el-menu-icon-width)}.el-sub-menu .el-icon.el-sub-menu__icon-more{margin-right:0!important}.el-sub-menu .el-sub-menu__icon-arrow{font-size:12px;margin-right:0;margin-top:-6px;position:absolute;right:var(--el-menu-base-level-padding);top:50%;transition:transform var(--el-transition-duration);width:inherit}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{color:var(--el-text-color-secondary);font-size:12px;line-height:normal;padding:7px 0 7px var(--el-menu-base-level-padding)}.horizontal-collapse-transition .el-sub-menu__title .el-sub-menu__icon-arrow{opacity:0;transition:var(--el-transition-duration-fast)}
diff --git a/themes/2023/assets/AdminView-BfUHrcUB.js b/themes/2023/assets/AdminView-BfUHrcUB.js
deleted file mode 100644
index 38b2218ca..000000000
--- a/themes/2023/assets/AdminView-BfUHrcUB.js
+++ /dev/null
@@ -1 +0,0 @@
-import{k as ie,T as Ke,t as ce,E as Qe,b as Xe}from"./el-input-CNNwOXGP.js";import{E as Ye,a as Ze}from"./el-form-item-llYGp2SQ.js";import"./el-tooltip-l0sNRNKZ.js";import{E as Pe,C as Je}from"./el-popper-C96qJ_vz.js";import{u as et,a as tt}from"./index-CBpXPsVN.js";import{_ as V,a as y,u as $,g as X,o as S,w,r as k,aC as Oe,aP as nt,j as h,T as Ne,l as ye,aG as at,c as f,P as A,h as D,n as Ie,ak as q,L as P,I as ue,aQ as ot,D as ve,b as Ce,i as de,s as x,aR as st,aS as lt,t as Ee,$ as re,p as _e,aT as Se,a0 as we,a1 as Be,z as I,W as Ae,G as Y,A as ze,C as ut,ab as pe,aU as $e,aV as ne,al as He,f as it,aW as rt,am as ke,d as me,aX as ct,m as dt,F as mt,aY as pt,M as Le,N as Me,aA as ft,U as ae,R as oe,aK as vt,a8 as ht,B as L,a9 as gt,ac as bt,af as _t,aF as Te}from"./index-CxMsK_Ni.js";import{f as Mt}from"./vnode-Cc0RQjVK.js";import"./_baseClone-DD6IrDYX.js";const yt=y({name:"ElCollapseTransition"}),It=y({...yt,setup(e){const a=$("collapse-transition"),n=t=>{t.style.maxHeight="",t.style.overflow=t.dataset.oldOverflow,t.style.paddingTop=t.dataset.oldPaddingTop,t.style.paddingBottom=t.dataset.oldPaddingBottom},o={beforeEnter(t){t.dataset||(t.dataset={}),t.dataset.oldPaddingTop=t.style.paddingTop,t.dataset.oldPaddingBottom=t.style.paddingBottom,t.style.height&&(t.dataset.elExistsHeight=t.style.height),t.style.maxHeight=0,t.style.paddingTop=0,t.style.paddingBottom=0},enter(t){requestAnimationFrame(()=>{t.dataset.oldOverflow=t.style.overflow,t.dataset.elExistsHeight?t.style.maxHeight=t.dataset.elExistsHeight:t.scrollHeight!==0?t.style.maxHeight=`${t.scrollHeight}px`:t.style.maxHeight=0,t.style.paddingTop=t.dataset.oldPaddingTop,t.style.paddingBottom=t.dataset.oldPaddingBottom,t.style.overflow="hidden"})},afterEnter(t){t.style.maxHeight="",t.style.overflow=t.dataset.oldOverflow},enterCancelled(t){n(t)},beforeLeave(t){t.dataset||(t.dataset={}),t.dataset.oldPaddingTop=t.style.paddingTop,t.dataset.oldPaddingBottom=t.style.paddingBottom,t.dataset.oldOverflow=t.style.overflow,t.style.maxHeight=`${t.scrollHeight}px`,t.style.overflow="hidden"},leave(t){t.scrollHeight!==0&&(t.style.maxHeight=0,t.style.paddingTop=0,t.style.paddingBottom=0)},afterLeave(t){n(t)},leaveCancelled(t){n(t)}};return(t,v)=>(S(),X(Ne,Oe({name:h(a).b()},nt(o)),{default:w(()=>[k(t.$slots,"default")]),_:3},16,["name"]))}});var Ct=V(It,[["__file","collapse-transition.vue"]]);const Et=ye(Ct),St=y({name:"ElContainer"}),wt=y({...St,props:{direction:{type:String}},setup(e){const a=e,n=at(),o=$("container"),t=f(()=>a.direction==="vertical"?!0:a.direction==="horizontal"?!1:n&&n.default?n.default().some(d=>{const g=d.type.name;return g==="ElHeader"||g==="ElFooter"}):!1);return(v,d)=>(S(),A("section",{class:D([h(o).b(),h(o).is("vertical",h(t))])},[k(v.$slots,"default")],2))}});var xt=V(wt,[["__file","container.vue"]]);const $t=y({name:"ElAside"}),kt=y({...$t,props:{width:{type:String,default:null}},setup(e){const a=e,n=$("aside"),o=f(()=>a.width?n.cssVarBlock({width:a.width}):{});return(t,v)=>(S(),A("aside",{class:D(h(n).b()),style:Ie(h(o))},[k(t.$slots,"default")],6))}});var De=V(kt,[["__file","aside.vue"]]);const Tt=y({name:"ElFooter"}),Pt=y({...Tt,props:{height:{type:String,default:null}},setup(e){const a=e,n=$("footer"),o=f(()=>a.height?n.cssVarBlock({height:a.height}):{});return(t,v)=>(S(),A("footer",{class:D(h(n).b()),style:Ie(h(o))},[k(t.$slots,"default")],6))}});var Ve=V(Pt,[["__file","footer.vue"]]);const Ot=y({name:"ElHeader"}),Nt=y({...Ot,props:{height:{type:String,default:null}},setup(e){const a=e,n=$("header"),o=f(()=>a.height?n.cssVarBlock({height:a.height}):{});return(t,v)=>(S(),A("header",{class:D(h(n).b()),style:Ie(h(o))},[k(t.$slots,"default")],6))}});var Fe=V(Nt,[["__file","header.vue"]]);const Bt=y({name:"ElMain"}),At=y({...Bt,setup(e){const a=$("main");return(n,o)=>(S(),A("main",{class:D(h(a).b())},[k(n.$slots,"default")],2))}});var Re=V(At,[["__file","main.vue"]]);const zt=ye(xt,{Aside:De,Footer:Ve,Header:Fe,Main:Re});q(De);q(Ve);const Ht=q(Fe),Lt=q(Re);let Dt=class{constructor(a,n){this.parent=a,this.domNode=n,this.subIndex=0,this.subIndex=0,this.init()}init(){this.subMenuItems=this.domNode.querySelectorAll("li"),this.addListeners()}gotoSubIndex(a){a===this.subMenuItems.length?a=0:a<0&&(a=this.subMenuItems.length-1),this.subMenuItems[a].focus(),this.subIndex=a}addListeners(){const a=this.parent.domNode;Array.prototype.forEach.call(this.subMenuItems,n=>{n.addEventListener("keydown",o=>{let t=!1;switch(o.code){case P.down:{this.gotoSubIndex(this.subIndex+1),t=!0;break}case P.up:{this.gotoSubIndex(this.subIndex-1),t=!0;break}case P.tab:{ie(a,"mouseleave");break}case P.enter:case P.numpadEnter:case P.space:{t=!0,o.currentTarget.click();break}}return t&&(o.preventDefault(),o.stopPropagation()),!1})})}},Vt=class{constructor(a,n){this.domNode=a,this.submenu=null,this.submenu=null,this.init(n)}init(a){this.domNode.setAttribute("tabindex","0");const n=this.domNode.querySelector(`.${a}-menu`);n&&(this.submenu=new Dt(this,n)),this.addListeners()}addListeners(){this.domNode.addEventListener("keydown",a=>{let n=!1;switch(a.code){case P.down:{ie(a.currentTarget,"mouseenter"),this.submenu&&this.submenu.gotoSubIndex(0),n=!0;break}case P.up:{ie(a.currentTarget,"mouseenter"),this.submenu&&this.submenu.gotoSubIndex(this.submenu.subMenuItems.length-1),n=!0;break}case P.tab:{ie(a.currentTarget,"mouseleave");break}case P.enter:case P.numpadEnter:case P.space:{n=!0,a.currentTarget.click();break}}n&&a.preventDefault()})}},Ft=class{constructor(a,n){this.domNode=a,this.init(n)}init(a){const n=this.domNode.childNodes;Array.from(n).forEach(o=>{o.nodeType===1&&new Vt(o,a)})}};const Rt=y({name:"ElMenuCollapseTransition",setup(){const e=$("menu");return{listeners:{onBeforeEnter:n=>n.style.opacity="0.2",onEnter(n,o){ue(n,`${e.namespace.value}-opacity-transition`),n.style.opacity="1",o()},onAfterEnter(n){ve(n,`${e.namespace.value}-opacity-transition`),n.style.opacity=""},onBeforeLeave(n){n.dataset||(n.dataset={}),ot(n,e.m("collapse"))?(ve(n,e.m("collapse")),n.dataset.oldOverflow=n.style.overflow,n.dataset.scrollWidth=n.clientWidth.toString(),ue(n,e.m("collapse"))):(ue(n,e.m("collapse")),n.dataset.oldOverflow=n.style.overflow,n.dataset.scrollWidth=n.clientWidth.toString(),ve(n,e.m("collapse"))),n.style.width=`${n.scrollWidth}px`,n.style.overflow="hidden"},onLeave(n){ue(n,"horizontal-collapse-transition"),n.style.width=`${n.dataset.scrollWidth}px`}}}}});function Wt(e,a,n,o,t,v){return S(),X(Ne,Oe({mode:"out-in"},e.listeners),{default:w(()=>[k(e.$slots,"default")]),_:3},16)}var jt=V(Rt,[["render",Wt],["__file","menu-collapse-transition.vue"]]);function We(e,a){const n=f(()=>{let t=e.parent;const v=[a.value];for(;t.type.name!=="ElMenu";)t.props.index&&v.unshift(t.props.index),t=t.parent;return v});return{parentMenu:f(()=>{let t=e.parent;for(;t&&!["ElMenu","ElSubMenu"].includes(t.type.name);)t=t.parent;return t}),indexPath:n}}function qt(e){return f(()=>{const n=e.backgroundColor;return n?new Ke(n).shade(20).toString():""})}const je=(e,a)=>{const n=$("menu");return f(()=>n.cssVarBlock({"text-color":e.textColor||"","hover-text-color":e.textColor||"","bg-color":e.backgroundColor||"","hover-bg-color":qt(e).value||"","active-color":e.activeTextColor||"",level:`${a}`}))},Gt=Ce({index:{type:String,required:!0},showTimeout:Number,hideTimeout:Number,popperClass:String,disabled:Boolean,teleported:{type:Boolean,default:void 0},popperOffset:Number,expandCloseIcon:{type:ne},expandOpenIcon:{type:ne},collapseCloseIcon:{type:ne},collapseOpenIcon:{type:ne}}),he="ElSubMenu";var xe=y({name:he,props:Gt,setup(e,{slots:a,expose:n}){const o=Se(),{indexPath:t,parentMenu:v}=We(o,f(()=>e.index)),d=$("menu"),g=$("sub-menu"),i=de("rootMenu");i||ce(he,"can not inject root menu");const m=de(`subMenu:${v.value.uid}`);m||ce(he,"can not inject sub menu");const c=x({}),b=x({});let C;const O=x(!1),Z=x(),G=x(null),z=f(()=>W.value==="horizontal"&&H.value?"bottom-start":"right-start"),F=f(()=>W.value==="horizontal"&&H.value||W.value==="vertical"&&!i.props.collapse?e.expandCloseIcon&&e.expandOpenIcon?B.value?e.expandOpenIcon:e.expandCloseIcon:st:e.collapseCloseIcon&&e.collapseOpenIcon?B.value?e.collapseOpenIcon:e.collapseCloseIcon:lt),H=f(()=>m.level===0),R=f(()=>{const u=e.teleported;return u===void 0?H.value:u}),J=f(()=>i.props.collapse?`${d.namespace.value}-zoom-in-left`:`${d.namespace.value}-zoom-in-top`),N=f(()=>W.value==="horizontal"&&H.value?["bottom-start","bottom-end","top-start","top-end","right-start","left-start"]:["right-start","right","right-end","left-start","bottom-start","bottom-end","top-start","top-end"]),B=f(()=>i.openedMenus.includes(e.index)),U=f(()=>{let u=!1;return Object.values(c.value).forEach(p=>{p.active&&(u=!0)}),Object.values(b.value).forEach(p=>{p.active&&(u=!0)}),u}),W=f(()=>i.props.mode),K=Ee({index:e.index,indexPath:t,active:U}),ee=je(i.props,m.level+1),se=f(()=>{var u;return(u=e.popperOffset)!=null?u:i.props.popperOffset}),Q=f(()=>{var u;return(u=e.popperClass)!=null?u:i.props.popperClass}),le=f(()=>{var u;return(u=e.showTimeout)!=null?u:i.props.showTimeout}),fe=f(()=>{var u;return(u=e.hideTimeout)!=null?u:i.props.hideTimeout}),s=()=>{var u,p,E;return(E=(p=(u=G.value)==null?void 0:u.popperRef)==null?void 0:p.popperInstanceRef)==null?void 0:E.destroy()},l=u=>{u||s()},r=()=>{i.props.menuTrigger==="hover"&&i.props.mode==="horizontal"||i.props.collapse&&i.props.mode==="vertical"||e.disabled||i.handleSubMenuClick({index:e.index,indexPath:t.value,active:U.value})},_=(u,p=le.value)=>{var E;if(u.type!=="focus"){if(i.props.menuTrigger==="click"&&i.props.mode==="horizontal"||!i.props.collapse&&i.props.mode==="vertical"||e.disabled){m.mouseInChild.value=!0;return}m.mouseInChild.value=!0,C==null||C(),{stop:C}=$e(()=>{i.openMenu(e.index,t.value)},p),R.value&&((E=v.value.vnode.el)==null||E.dispatchEvent(new MouseEvent("mouseenter")))}},M=(u=!1)=>{var p;if(i.props.menuTrigger==="click"&&i.props.mode==="horizontal"||!i.props.collapse&&i.props.mode==="vertical"){m.mouseInChild.value=!1;return}C==null||C(),m.mouseInChild.value=!1,{stop:C}=$e(()=>!O.value&&i.closeMenu(e.index,t.value),fe.value),R.value&&u&&((p=m.handleMouseleave)==null||p.call(m,!0))};re(()=>i.props.collapse,u=>l(!!u));{const u=E=>{b.value[E.index]=E},p=E=>{delete b.value[E.index]};_e(`subMenu:${o.uid}`,{addSubMenu:u,removeSubMenu:p,handleMouseleave:M,mouseInChild:O,level:m.level+1})}return n({opened:B}),we(()=>{i.addSubMenu(K),m.addSubMenu(K)}),Be(()=>{m.removeSubMenu(K),i.removeSubMenu(K)}),()=>{var u;const p=[(u=a.title)==null?void 0:u.call(a),I(Ae,{class:g.e("icon-arrow"),style:{transform:B.value?e.expandCloseIcon&&e.expandOpenIcon||e.collapseCloseIcon&&e.collapseOpenIcon&&i.props.collapse?"none":"rotateZ(180deg)":"none"}},{default:()=>Y(F.value)?I(o.appContext.components[F.value]):I(F.value)})],E=i.isMenuPopup?I(Pe,{ref:G,visible:B.value,effect:"light",pure:!0,offset:se.value,showArrow:!1,persistent:!0,popperClass:Q.value,placement:z.value,teleported:R.value,fallbackPlacements:N.value,transition:J.value,gpuAcceleration:!1},{content:()=>{var T;return I("div",{class:[d.m(W.value),d.m("popup-container"),Q.value],onMouseenter:j=>_(j,100),onMouseleave:()=>M(!0),onFocus:j=>_(j,100)},[I("ul",{class:[d.b(),d.m("popup"),d.m(`popup-${z.value}`)],style:ee.value},[(T=a.default)==null?void 0:T.call(a)])])},default:()=>I("div",{class:g.e("title"),onClick:r},p)}):I(pe,{},[I("div",{class:g.e("title"),ref:Z,onClick:r},p),I(Et,{},{default:()=>{var T;return ze(I("ul",{role:"menu",class:[d.b(),d.m("inline")],style:ee.value},[(T=a.default)==null?void 0:T.call(a)]),[[ut,B.value]])}})]);return I("li",{class:[g.b(),g.is("active",U.value),g.is("opened",B.value),g.is("disabled",e.disabled)],role:"menuitem",ariaHaspopup:!0,ariaExpanded:B.value,onMouseenter:_,onMouseleave:()=>M(),onFocus:_},[E])}}});const Ut=Ce({mode:{type:String,values:["horizontal","vertical"],default:"vertical"},defaultActive:{type:String,default:""},defaultOpeneds:{type:me(Array),default:()=>dt([])},uniqueOpened:Boolean,router:Boolean,menuTrigger:{type:String,values:["hover","click"],default:"hover"},collapse:Boolean,backgroundColor:String,textColor:String,activeTextColor:String,closeOnClickOutside:Boolean,collapseTransition:{type:Boolean,default:!0},ellipsis:{type:Boolean,default:!0},popperOffset:{type:Number,default:6},ellipsisIcon:{type:ne,default:()=>ct},popperEffect:{type:me(String),default:"dark"},popperClass:String,showTimeout:{type:Number,default:300},hideTimeout:{type:Number,default:300}}),ge=e=>He(e)&&e.every(a=>Y(a)),Kt={close:(e,a)=>Y(e)&&ge(a),open:(e,a)=>Y(e)&&ge(a),select:(e,a,n,o)=>Y(e)&&ge(a)&&it(n)&&(o===void 0||o instanceof Promise)};var Qt=y({name:"ElMenu",props:Ut,emits:Kt,setup(e,{emit:a,slots:n,expose:o}){const t=Se(),v=t.appContext.config.globalProperties.$router,d=x(),g=$("menu"),i=$("sub-menu"),m=x(-1),c=x(e.defaultOpeneds&&!e.collapse?e.defaultOpeneds.slice(0):[]),b=x(e.defaultActive),C=x({}),O=x({}),Z=f(()=>e.mode==="horizontal"||e.mode==="vertical"&&e.collapse),G=()=>{const s=b.value&&C.value[b.value];if(!s||e.mode==="horizontal"||e.collapse)return;s.indexPath.forEach(r=>{const _=O.value[r];_&&z(r,_.indexPath)})},z=(s,l)=>{c.value.includes(s)||(e.uniqueOpened&&(c.value=c.value.filter(r=>l.includes(r))),c.value.push(s),a("open",s,l))},F=s=>{const l=c.value.indexOf(s);l!==-1&&c.value.splice(l,1)},H=(s,l)=>{F(s),a("close",s,l)},R=({index:s,indexPath:l})=>{c.value.includes(s)?H(s,l):z(s,l)},J=s=>{(e.mode==="horizontal"||e.collapse)&&(c.value=[]);const{index:l,indexPath:r}=s;if(!(ke(l)||ke(r)))if(e.router&&v){const _=s.route||l,M=v.push(_).then(u=>(u||(b.value=l),u));a("select",l,r,{index:l,indexPath:r,route:_},M)}else b.value=l,a("select",l,r,{index:l,indexPath:r})},N=s=>{const l=C.value,r=l[s]||b.value&&l[b.value]||l[e.defaultActive];r?b.value=r.index:b.value=s},B=s=>{const l=getComputedStyle(s),r=Number.parseInt(l.marginLeft,10),_=Number.parseInt(l.marginRight,10);return s.offsetWidth+r+_||0},U=()=>{var s,l;if(!d.value)return-1;const r=Array.from((l=(s=d.value)==null?void 0:s.childNodes)!=null?l:[]).filter(te=>te.nodeName!=="#text"||te.nodeValue),_=64,M=getComputedStyle(d.value),u=Number.parseInt(M.paddingLeft,10),p=Number.parseInt(M.paddingRight,10),E=d.value.clientWidth-u-p;let T=0,j=0;return r.forEach((te,Ue)=>{te.nodeName!=="#comment"&&(T+=B(te),T<=E-_&&(j=Ue+1))}),j===r.length?-1:j},W=s=>O.value[s].indexPath,K=(s,l=33.34)=>{let r;return()=>{r&&clearTimeout(r),r=setTimeout(()=>{s()},l)}};let ee=!0;const se=()=>{if(m.value===U())return;const s=()=>{m.value=-1,mt(()=>{m.value=U()})};ee?s():K(s)(),ee=!1};re(()=>e.defaultActive,s=>{C.value[s]||(b.value=""),N(s)}),re(()=>e.collapse,s=>{s&&(c.value=[])}),re(C.value,G);let Q;rt(()=>{e.mode==="horizontal"&&e.ellipsis?Q=pt(d,se).stop:Q==null||Q()});const le=x(!1);{const s=M=>{O.value[M.index]=M},l=M=>{delete O.value[M.index]};_e("rootMenu",Ee({props:e,openedMenus:c,items:C,subMenus:O,activeIndex:b,isMenuPopup:Z,addMenuItem:M=>{C.value[M.index]=M},removeMenuItem:M=>{delete C.value[M.index]},addSubMenu:s,removeSubMenu:l,openMenu:z,closeMenu:H,handleMenuItemClick:J,handleSubMenuClick:R})),_e(`subMenu:${t.uid}`,{addSubMenu:s,removeSubMenu:l,mouseInChild:le,level:0})}we(()=>{e.mode==="horizontal"&&new Ft(t.vnode.el,g.namespace.value)}),o({open:l=>{const{indexPath:r}=O.value[l];r.forEach(_=>z(_,r))},close:F,handleResize:se});const fe=je(e,0);return()=>{var s,l;let r=(l=(s=n.default)==null?void 0:s.call(n))!=null?l:[];const _=[];if(e.mode==="horizontal"&&d.value){const p=Mt(r),E=m.value===-1?p:p.slice(0,m.value),T=m.value===-1?[]:p.slice(m.value);T!=null&&T.length&&e.ellipsis&&(r=E,_.push(I(xe,{index:"sub-menu-more",class:i.e("hide-arrow"),popperOffset:e.popperOffset},{title:()=>I(Ae,{class:i.e("icon-more")},{default:()=>I(e.ellipsisIcon)}),default:()=>T})))}const M=e.closeOnClickOutside?[[Je,()=>{c.value.length&&(le.value||(c.value.forEach(p=>a("close",p,W(p))),c.value=[]))}]]:[],u=ze(I("ul",{key:String(e.collapse),role:"menubar",ref:d,style:fe.value,class:{[g.b()]:!0,[g.m(e.mode)]:!0,[g.m("collapse")]:e.collapse}},[...r,..._]),M);return e.collapseTransition&&e.mode==="vertical"?I(jt,()=>u):u}}});const Xt=Ce({index:{type:me([String,null]),default:null},route:{type:me([String,Object])},disabled:Boolean}),Yt={click:e=>Y(e.index)&&He(e.indexPath)},be="ElMenuItem",Zt=y({name:be,components:{ElTooltip:Pe},props:Xt,emits:Yt,setup(e,{emit:a}){const n=Se(),o=de("rootMenu"),t=$("menu"),v=$("menu-item");o||ce(be,"can not inject root menu");const{parentMenu:d,indexPath:g}=We(n,ft(e,"index")),i=de(`subMenu:${d.value.uid}`);i||ce(be,"can not inject sub menu");const m=f(()=>e.index===o.activeIndex),c=Ee({index:e.index,indexPath:g,active:m}),b=()=>{e.disabled||(o.handleMenuItemClick({index:e.index,indexPath:g.value,route:e.route}),a("click",c))};return we(()=>{i.addSubMenu(c),o.addMenuItem(c)}),Be(()=>{i.removeSubMenu(c),o.removeMenuItem(c)}),{parentMenu:d,rootMenu:o,active:m,nsMenu:t,nsMenuItem:v,handleClick:b}}});function Jt(e,a,n,o,t,v){const d=Le("el-tooltip");return S(),A("li",{class:D([e.nsMenuItem.b(),e.nsMenuItem.is("active",e.active),e.nsMenuItem.is("disabled",e.disabled)]),role:"menuitem",tabindex:"-1",onClick:e.handleClick},[e.parentMenu.type.name==="ElMenu"&&e.rootMenu.props.collapse&&e.$slots.title?(S(),X(d,{key:0,effect:e.rootMenu.props.popperEffect,placement:"right","fallback-placements":["left"],persistent:""},{content:w(()=>[k(e.$slots,"title")]),default:w(()=>[Me("div",{class:D(e.nsMenu.be("tooltip","trigger"))},[k(e.$slots,"default")],2)]),_:3},8,["effect"])):(S(),A(pe,{key:1},[k(e.$slots,"default"),k(e.$slots,"title")],64))],10,["onClick"])}var qe=V(Zt,[["render",Jt],["__file","menu-item.vue"]]);const en={title:String},tn="ElMenuItemGroup",nn=y({name:tn,props:en,setup(){return{ns:$("menu-item-group")}}});function an(e,a,n,o,t,v){return S(),A("li",{class:D(e.ns.b())},[Me("div",{class:D(e.ns.e("title"))},[e.$slots.title?k(e.$slots,"title",{key:1}):(S(),A(pe,{key:0},[ae(oe(e.title),1)],64))],2),Me("ul",null,[k(e.$slots,"default")])],2)}var Ge=V(nn,[["render",an],["__file","menu-item-group.vue"]]);const on=ye(Qt,{MenuItem:qe,MenuItemGroup:Ge,SubMenu:xe}),sn=q(qe);q(Ge);q(xe);const ln=vt("adminData",()=>{const e=x(localStorage.getItem("adminPassword")||""),a=x(!!localStorage.getItem("adminPassword"));function n(o){e.value=o,a.value=!0,localStorage.setItem("adminPassword",o)}return{adminPassword:e,updateAdminPwd:n,isAdmin:a}}),_n=y({__name:"AdminView",setup(e){const a=et(),n=tt(a),{t:o}=ht(),t=ln(),v=gt(),d=x([{name:o("admin.menu.fileManage"),path:"/admin"},{name:o("admin.menu.systemSetting"),path:"/admin/setting"},{name:o("admin.menu.local"),path:"/admin/local"},{name:o("admin.menu.about"),path:"/admin/about"},{name:o("admin.menu.send"),path:"/#/send"},{name:o("admin.menu.receive"),path:"/#/"}]),g=()=>{_t({url:"/admin/login",method:"post",data:{password:t.adminPassword}}).then(m=>{m.code===200?(t.updateAdminPwd(m.detail.token),Te.success(o("admin.login.loginSuccess"))):(localStorage.clear(),Te.error(o("admin.login.loginError")))})},i=()=>{localStorage.clear()};return(m,c)=>{const b=sn,C=on,O=Ht,Z=Le("router-view"),G=Lt,z=zt,F=Xe,H=Qe,R=Ze,J=Ye;return h(t).isAdmin?(S(),X(z,{key:0,style:{height:"100vh",width:"100vw",position:"relative","user-select":"none"}},{default:w(()=>[L(O,null,{default:w(()=>[L(C,{mode:"horizontal",router:"","default-active":h(v).path},{default:w(()=>[(S(!0),A(pe,null,bt(d.value,N=>(S(),X(b,{index:N.path,key:N.path},{default:w(()=>[ae(oe(N.name),1)]),_:2},1032,["index"]))),128)),L(b,{style:{float:"right"},onClick:c[0]||(c[0]=N=>h(n)(!h(a)))},{default:w(()=>[ae(oe(h(o)("admin.menu.color")),1)]),_:1}),L(b,{style:{float:"right"},onClick:c[1]||(c[1]=N=>i())},{default:w(()=>[ae(oe(h(o)("admin.menu.signout")),1)]),_:1})]),_:1},8,["default-active"])]),_:1}),L(G,null,{default:w(()=>[L(Z)]),_:1})]),_:1})):(S(),X(J,{key:1,size:"large"},{default:w(()=>[L(R,{label:h(o)("admin.login.managePassword")},{default:w(()=>[L(H,{modelValue:h(t).adminPassword,"onUpdate:modelValue":c[2]||(c[2]=N=>h(t).adminPassword=N),placeholder:h(o)("admin.login.passwordNotEmpty"),type:"password"},{append:w(()=>[L(F,{onClick:g},{default:w(()=>[ae(oe(h(o)("admin.login.login")),1)]),_:1})]),_:1},8,["modelValue","placeholder"])]),_:1},8,["label"])]),_:1}))}}});export{_n as default};
diff --git a/themes/2023/assets/CardTools-BeU-pM3r.css b/themes/2023/assets/CardTools-BeU-pM3r.css
deleted file mode 100644
index 98912a299..000000000
--- a/themes/2023/assets/CardTools-BeU-pM3r.css
+++ /dev/null
@@ -1 +0,0 @@
-.el-drawer{--el-drawer-bg-color:var(--el-dialog-bg-color,var(--el-bg-color));--el-drawer-padding-primary:var(--el-dialog-padding-primary,20px);background-color:var(--el-drawer-bg-color);box-shadow:var(--el-box-shadow-dark);box-sizing:border-box;display:flex;flex-direction:column;overflow:hidden;position:absolute;transition:all var(--el-transition-duration)}.el-drawer .btt,.el-drawer .ltr,.el-drawer .rtl,.el-drawer .ttb{transform:translate(0)}.el-drawer__sr-focus:focus{outline:none!important}.el-drawer__header{align-items:center;color:#72767b;display:flex;margin-bottom:32px;padding:var(--el-drawer-padding-primary);padding-bottom:0}.el-drawer__header>:first-child{flex:1}.el-drawer__title{flex:1;font-size:16px;line-height:inherit;margin:0}.el-drawer__footer{padding:var(--el-drawer-padding-primary);padding-top:10px;text-align:right}.el-drawer__close-btn{background-color:transparent;border:none;color:inherit;cursor:pointer;display:inline-flex;font-size:var(--el-font-size-extra-large);outline:none}.el-drawer__close-btn:focus i,.el-drawer__close-btn:hover i{color:var(--el-color-primary)}.el-drawer__body{flex:1;overflow:auto;padding:var(--el-drawer-padding-primary)}.el-drawer__body>*{box-sizing:border-box}.el-drawer.ltr,.el-drawer.rtl{bottom:0;height:100%;top:0}.el-drawer.btt,.el-drawer.ttb{left:0;right:0;width:100%}.el-drawer.ltr{left:0}.el-drawer.rtl{right:0}.el-drawer.ttb{top:0}.el-drawer.btt{bottom:0}.el-drawer-fade-enter-active,.el-drawer-fade-leave-active{transition:all var(--el-transition-duration)}.el-drawer-fade-enter-active,.el-drawer-fade-enter-from,.el-drawer-fade-enter-to,.el-drawer-fade-leave-active,.el-drawer-fade-leave-from,.el-drawer-fade-leave-to{overflow:hidden!important}.el-drawer-fade-enter-from,.el-drawer-fade-leave-to{background-color:transparent!important}.el-drawer-fade-enter-from .rtl,.el-drawer-fade-leave-to .rtl{transform:translate(100%)}.el-drawer-fade-enter-from .ltr,.el-drawer-fade-leave-to .ltr{transform:translate(-100%)}.el-drawer-fade-enter-from .ttb,.el-drawer-fade-leave-to .ttb{transform:translateY(-100%)}.el-drawer-fade-enter-from .btt,.el-drawer-fade-leave-to .btt{transform:translateY(100%)}.el-progress{align-items:center;display:flex;line-height:1;position:relative}.el-progress__text{color:var(--el-text-color-regular);font-size:14px;line-height:1;margin-left:5px;min-width:50px}.el-progress__text i{display:block;vertical-align:middle}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{left:0;margin:0;position:absolute;text-align:center;top:50%;transform:translateY(-50%);width:100%}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{display:inline-block;vertical-align:middle}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{display:block;margin-right:0;padding-right:0}.el-progress--text-inside .el-progress-bar{margin-right:0;padding-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:var(--el-color-success)}.el-progress.is-success .el-progress__text{color:var(--el-color-success)}.el-progress.is-warning .el-progress-bar__inner{background-color:var(--el-color-warning)}.el-progress.is-warning .el-progress__text{color:var(--el-color-warning)}.el-progress.is-exception .el-progress-bar__inner{background-color:var(--el-color-danger)}.el-progress.is-exception .el-progress__text{color:var(--el-color-danger)}.el-progress-bar{box-sizing:border-box;flex-grow:1}.el-progress-bar__outer{background-color:var(--el-border-color-lighter);border-radius:100px;height:6px;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{background-color:var(--el-color-primary);border-radius:100px;height:100%;left:0;line-height:1;position:absolute;text-align:right;top:0;transition:width .6s ease;white-space:nowrap}.el-progress-bar__inner:after{content:"";display:inline-block;height:100%;vertical-align:middle}.el-progress-bar__inner--indeterminate{animation:indeterminate 3s infinite;transform:translateZ(0)}.el-progress-bar__inner--striped{background-image:linear-gradient(45deg,rgba(0,0,0,.1) 25%,transparent 0,transparent 50%,rgba(0,0,0,.1) 0,rgba(0,0,0,.1) 75%,transparent 0,transparent);background-size:1.25em 1.25em}.el-progress-bar__inner--striped.el-progress-bar__inner--striped-flow{animation:striped-flow 3s linear infinite}.el-progress-bar__innerText{color:#fff;display:inline-block;font-size:12px;margin:0 5px;vertical-align:middle}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}@keyframes indeterminate{0%{left:-100%}to{left:100%}}@keyframes striped-flow{0%{background-position:-100%}to{background-position:100%}}@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2) format("woff2"),url(/assets/KaTeX_AMS-Regular-DMm9YOAa.woff) format("woff"),url(/assets/KaTeX_AMS-Regular-DRggAlZN.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2) format("woff2"),url(/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff) format("woff"),url(/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2) format("woff2"),url(/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff) format("woff"),url(/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2) format("woff2"),url(/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff) format("woff"),url(/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2) format("woff2"),url(/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff) format("woff"),url(/assets/KaTeX_Fraktur-Regular-CB_wures.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(/assets/KaTeX_Main-Bold-Cx986IdX.woff2) format("woff2"),url(/assets/KaTeX_Main-Bold-Jm3AIy58.woff) format("woff"),url(/assets/KaTeX_Main-Bold-waoOVXN0.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2) format("woff2"),url(/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff) format("woff"),url(/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2) format("woff2"),url(/assets/KaTeX_Main-Italic-BMLOBm91.woff) format("woff"),url(/assets/KaTeX_Main-Italic-3WenGoN9.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Main-Regular-B22Nviop.woff2) format("woff2"),url(/assets/KaTeX_Main-Regular-Dr94JaBh.woff) format("woff"),url(/assets/KaTeX_Main-Regular-ypZvNtVU.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2) format("woff2"),url(/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff) format("woff"),url(/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(/assets/KaTeX_Math-Italic-t53AETM-.woff2) format("woff2"),url(/assets/KaTeX_Math-Italic-DA0__PXp.woff) format("woff"),url(/assets/KaTeX_Math-Italic-flOr_0UB.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:700;src:url(/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2) format("woff2"),url(/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff) format("woff"),url(/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:italic;font-weight:400;src:url(/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2) format("woff2"),url(/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff) format("woff"),url(/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:400;src:url(/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2) format("woff2"),url(/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff) format("woff"),url(/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf) format("truetype")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Script-Regular-D3wIWfF6.woff2) format("woff2"),url(/assets/KaTeX_Script-Regular-D5yQViql.woff) format("woff"),url(/assets/KaTeX_Script-Regular-C5JkGWo-.ttf) format("truetype")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2) format("woff2"),url(/assets/KaTeX_Size1-Regular-C195tn64.woff) format("woff"),url(/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf) format("truetype")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2) format("woff2"),url(/assets/KaTeX_Size2-Regular-oD1tc_U0.woff) format("woff"),url(/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf) format("truetype")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(data:font/woff2;base64,d09GMgABAAAAAA4oAA4AAAAAHbQAAA3TAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAgRQIDgmcDBEICo1oijYBNgIkA14LMgAEIAWJAAeBHAyBHBvbGiMRdnO0IkRRkiYDgr9KsJ1NUAf2kILNxgUmgqIgq1P89vcbIcmsQbRps3vCcXdYOKSWEPEKgZgQkprQQsxIXUgq0DqpGKmIvrgkeVGtEQD9DzAO29fM9jYhxZEsL2FeURH2JN4MIcTdO049NCVdxQ/w9NrSYFEBKTDKpLKfNkCGDc1RwjZLQcm3vqJ2UW9Xfa3tgAHz6ivp6vgC2yD4/6352ndnN0X0TL7seypkjZlMsjmZnf0Mm5Q+JykRWQBKCVCVPbARPXWyQtb5VgLB6Biq7/Uixcj2WGqdI8tGSgkuRG+t910GKP2D7AQH0DB9FMDW/obJZ8giFI3Wg8Cvevz0M+5m0rTh7XDBlvo9Y4vm13EXmfttwI4mBo1EG15fxJhUiCLbiiyCf/ZA6MFAhg3pGIZGdGIVjtPn6UcMk9A/UUr9PhoNsCENw1APAq0gpH73e+M+0ueyHbabc3vkbcdtzcf/fiy+NxQEjf9ud/ELBHAXJ0nk4z+MXH2Ev/kWyV4k7SkvpPc9Qr38F6RPWnM9cN6DJ0AdD1BhtgABtmoRoFCvPsBAumNm6soZG2Gk5GyVTo2sJncSyp0jQTYoR6WDvTwaaEcHsxHfvuWhHA3a6bN7twRKtcGok6NsCi7jYRrM2jExsUFMxMQYuJbMhuWNOumEJy9hi29Dmg5zMp/A5+hhPG19j1vBrq8JTLr8ki5VLPmG/PynJHVul440bxg5xuymHUFPBshC+nA9I1FmwbRBTNHAcik3Oae0cxKoI3MOriM42UrPe51nsaGxJ+WfXubAsP84aabUlQSJ1IiE0iPETLUU4CATgfXSCSpuRFRmCGbO+wSpAnzaeaCYW1VNEysRtuXCEL1kUFUbbtMv3Tilt/1c11jt3Q5bbMa84cpWipp8Elw3MZhOHsOlwwVUQM3lAR35JiFQbaYCRnMF2lxAWoOg2gyoIV4PouX8HytNIfLhqpJtXB4vjiViUI8IJ7bkC4ikkQvKksnOTKICwnqWSZ9YS5f0WCxmpgjbIq7EJcM4aI2nmhLNY2JIUgOjXZFWBHb+x5oh6cwb0Tv1ackHdKi0I9OO2wE9aogIOn540CCCziyhN+IaejtgAONKznHlHyutPrHGwCx9S6B8kfS4Mfi4Eyv7OU730bT1SCBjt834cXsf43zVjPUqqJjgrjeGnBxSG4aYAKFuVbeCfkDIjAqMb6yLNIbCuvXhMH2/+k2vkNpkORhR59N1CkzoOENvneIosjYmuTxlhUzaGEJQ/iWqx4dmwpmKjrwTiTGTCVozNAYqk/zXOndWxuWSmJkQpJw3pK5KX6QrLt5LATMqpmPAQhkhK6PUjzHUn7E0gHE0kPE0iKkolgkUx9SZmVAdDgpffdyJKg3k7VmzYGCwVXGz/tXmkOIp+vcWs+EMuhhvN0h9uhfzWJziBQmCREGSIFmQIkgVpAnSBRmC//6hkLZwaVhwxlrJSOdqlFtOYxlau9F2QN5Y98xmIAsiM1HVp2VFX+DHHGg6Ecjh3vmqtidX3qHI2qycTk/iwxSt5UzTmEP92ZBnEWTk4Mx8Mpl78ZDokxg/KWb+Q0QkvdKVmq3TMW+RXEgrsziSAfNXFMhDc60N5N9jQzjfO0kBKpUZl0ZmwJ41j/B9Hz6wmRaJB84niNmQrzp9eSlQCDDzazGDdVi3P36VZQ+Jy4f9UBNp+3zTjqI4abaFAm+GShVaXlsGdF3FYzZcDI6cori4kMxUECl9IjJZpzkvitAoxKue+90pDMvcKRxLl53TmOKCmV/xRolNKSqqUxc6LStOETmFOiLZZptlZepcKiAzteG8PEdpnQpbOMNcMsR4RR2Bs0cKFEvSmIjAFcnarqwUL4lDhHmnVkwu1IwshbiCcgvOheZuYyOteufZZwlcTlLgnZ3o/WcYdzZHW/WGaqaVfmTZ1aWCceJjkbZqsfbkOtcFlUZM/jy+hXHDbaUobWqqXaeWobbLO99yG5N3U4wxco0rQGGcOLASFMXeJoham8M+/x6O2WywK2l4HGbq1CoUyC/IZikQhdq3SiuNrvAEj0AVu9x2x3lp/xWzahaxidezFVtdcb5uEnzyl0ZmYiuKI0exvCd4Xc9CV1KB0db00z92wDPde0kukbvZIWN6jUWFTmPIC/Y4UPCm8UfDTFZpZNon1qLFTkBhxzB+FjQRA2Q/YRJT8pQigslMaUpFyAG8TMlXigiqmAZX4xgijKjRlGpLE0GdplRfCaJo0JQaSxNBk6ZmMzcya0FmrcisDdn0Q3HI2sWSppYigmlM1XT/kLQZSNpMJG0WkjYbSZuDpM1F0uYhFc1HxU4m1QJjDK6iL0S5uSj5rgXc3RejEigtcRBtqYPQsiTskmO5vosV+q4VGIKbOkDg0jtRrq+Em1YloaTFar3EGr1EUC8R0kus1Uus00usL97ABr2BjXoDm/QGNhuWtMVBKOwg/i78lT7hBsAvDmwHc/ao3vmUbBmhjeYySZNWvGkfZAgISDSaDo1SVpzGDsAEkF8B+gEapViUoZgUWXcRIGFZNm6gWbAKk0bp0k1MHG9fLYtV4iS2SmLEQFARzRcnf9PUS0LVn05/J9MiRRBU3v2IrvW974v4N00L7ZMk0wXP1409CHo/an8zTRHD3eSJ6m8D4YMkZNl3M79sqeuAsr/m3f+8/yl7A50aiAEJgeBeMWzu7ui9UfUBCe2TIqZIoOd/3/udRBOQidQZUERzb2/VwZN1H/Sju82ew2H2Wfr6qvfVf3hqwDvAIpkQVFy4B9Pe9e4/XvPeceu7h3dvO56iJPf0+A6cqA2ip18ER+iFgggiuOkvj24bby0N9j2UHIkgqIt+sVgfodC4YghLSMjSZbH0VR/6dMDrYJeKHilKTemt6v6kvzvn3/RrdWtr0GoN/xL+Sex/cPYLUpepx9cz/D46UPU5KXgAQa+NDps1v6J3xP1i2HtaDB0M9aX2deA7SYff//+gUCovMmIK/qfsFcOk+4Y5ZN97XlG6zebqtMbKgeRFi51vnxTQYBUik2rS/Cn6PC8ADR8FGxsRPB82dzfND90gIcshOcYUkfjherBz53odpm6TP8txlwOZ71xmfHHOvq053qFF/MRlS3jP0ELudrf2OeN8DHvp6ZceLe8qKYvWz/7yp0u4dKPfli3CYq0O13Ih71mylJ80tOi10On8wi+F4+LWgDPeJ30msSQt9/vkmHq9/Lvo2b461mP801v3W4xTcs6CbvF9UDdrSt+A8OUbpSh55qAUFXWznBBfdeJ8a4d7ugT5tvxUza3h9m4H7ptTqiG4z0g5dc0X29OcGlhpGFMpQo9ytTS+NViZpNdvU4kWx+LKxNY10kQ1yqGXrhe4/1nvP7E+nd5A92TtaRplbHSqoIdOqtRWti+fkB5/n1+/VvCmz12pG1kpQWsfi1ftlBobm0bpngs16CHkbIwdLnParxtTV3QYRlfJ0KFskH7pdN/YDn+yRuSd7sNH3aO0DYPggk6uWuXrfOc+fa3VTxFVvKaNxHsiHmsXyCLIE5yuOeN3/Jdf8HBL/5M6shjyhxHx9BjB1O0+4NLOnjLLSxwO7ukN4jMbOIcD879KLSi6Pk61Oqm2377n8079PXEEQ7cy7OKEC9nbpet118fxweTafpt69x/Bt8UqGzNQt7aelpc44dn5cqhwf71+qKp/Zf/+a0zcizOUWpl/iBcSXip0pplkatCchoH5c5aUM8I7/dWxAej8WicPL1URFZ9BDJelUwEwTkGqUhgSlydVes95YdXvhh9Gfz/aeFWvgVb4tuLbcv4+wLdutVZv/cUonwBD/6eDlE0aSiKK/uoH3+J1wDE/jMVqY2ysGufN84oIXB0sPzy8ollX/LegY74DgJXJR57sn+VGza0x3DnuIgABFM15LmajjjsNlYj+JEZGbuRYcAMOWxFkPN2w6Wd46xo4gVWQR/X4lyI/R6K/YK0110GzudPRW7Y+UOBGTfNNzHeYT0fiH0taunBpq9HEW8OKSaBGj21L0MqenEmNRWBAWDWAk4CpNoEZJ2tTaPFgbQYj8HxtFilErs3BTRwT8uO1NXQaWfIotchmPkAF5mMBAliEmZiOGVgCG9LgRzpscMAOOwowlT3JhusdazXGSC/hxR3UlmWVwWHpOIKheqONvjyhSiTHIkVUco5bnji8m//zL7PKaT1Vl5I6UE609f+gkr6MZKVyKc7zJRmCahLsdlyA5fdQkRSan9LgnnLEyGSkaKJCJog0wAgvepWBt80+1yKln1bMVtCljfNWDueKLsWwaEbBSfSPTEmVRsUcYYMnEjcjeyCZzBXK9E9BYBXLKjOSpUDR+nEV3TFSUdQaz+ot98QxgXwx0GQ+EEUAKB2qZPkQQ0GqFD8UPFMqyaCHM24BZmSGic9EYMagKizOw9Hz50DMrDLrqqLkTAhplMictiCAx5S3BIUQdeJeLnBy2CNtMfz6cV4u8XKoFZQesbf9YZiIERiHjaNodDW6LgcirX/mPnJIkBGDUpTBhSa0EIr38D5hCIszhCM8URGBqImoWjpvpt1ebu/v3Gl3qJfMnNM+9V+kiRFyROTPHQWOcs1dNW94/ukKMPZBvDi55i5CttdeJz84DLngLqjcdwEZ87bFFR8CIG35OAkDVN6VRDZ7aq67NteYqZ2lpT8oYB2CytoBd6VuAx4WgiAsnuj3WohG+LugzXiQRDeM3XYXlULv4dp5VFYC) format("woff2"),url(/assets/KaTeX_Size3-Regular-CTq5MqoE.woff) format("woff"),url(/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf) format("truetype")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2) format("woff2"),url(/assets/KaTeX_Size4-Regular-BF-4gkZK.woff) format("woff"),url(/assets/KaTeX_Size4-Regular-DWFBv043.ttf) format("truetype")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2) format("woff2"),url(/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff) format("woff"),url(/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf) format("truetype")}.katex{font: 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.21"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}html.dark pre code.hljs{display:block;overflow-x:auto;padding:1em}html.dark code.hljs{padding:3px 5px}html.dark .hljs{color:#abb2bf;background:#282c34}html.dark .hljs-keyword,html.dark .hljs-operator,html.dark .hljs-pattern-match{color:#f92672}html.dark .hljs-function,html.dark .hljs-pattern-match .hljs-constructor{color:#61aeee}html.dark .hljs-function .hljs-params{color:#a6e22e}html.dark .hljs-function .hljs-params .hljs-typing{color:#fd971f}html.dark .hljs-module-access .hljs-module{color:#7e57c2}html.dark .hljs-constructor{color:#e2b93d}html.dark .hljs-constructor .hljs-string{color:#9ccc65}html.dark .hljs-comment,html.dark .hljs-quote{color:#b18eb1;font-style:italic}html.dark .hljs-doctag,html.dark .hljs-formula{color:#c678dd}html.dark .hljs-deletion,html.dark .hljs-name,html.dark .hljs-section,html.dark .hljs-selector-tag,html.dark .hljs-subst{color:#e06c75}html.dark .hljs-literal{color:#56b6c2}html.dark .hljs-addition,html.dark .hljs-attribute,html.dark .hljs-meta .hljs-string,html.dark .hljs-regexp,html.dark .hljs-string{color:#98c379}html.dark .hljs-built_in,html.dark .hljs-class .hljs-title,html.dark .hljs-title.class_{color:#e6c07b}html.dark .hljs-attr,html.dark .hljs-number,html.dark .hljs-selector-attr,html.dark .hljs-selector-class,html.dark .hljs-selector-pseudo,html.dark .hljs-template-variable,html.dark .hljs-type,html.dark .hljs-variable{color:#d19a66}html.dark .hljs-bullet,html.dark .hljs-link,html.dark .hljs-meta,html.dark .hljs-selector-id,html.dark .hljs-symbol,html.dark .hljs-title{color:#61aeee}html.dark .hljs-emphasis{font-style:italic}html.dark .hljs-strong{font-weight:700}html.dark .hljs-link{text-decoration:underline}html pre code.hljs{display:block;overflow-x:auto;padding:1em}html code.hljs{padding:3px 5px}html code.hljs::-webkit-scrollbar{height:4px}html .hljs{color:#383a42;background:#fafafa}html .hljs-comment,html .hljs-quote{color:#a0a1a7;font-style:italic}html .hljs-doctag,html .hljs-formula,html .hljs-keyword{color:#a626a4}html .hljs-deletion,html .hljs-name,html .hljs-section,html .hljs-selector-tag,html .hljs-subst{color:#e45649}html .hljs-literal{color:#0184bb}html .hljs-addition,html .hljs-attribute,html .hljs-meta .hljs-string,html .hljs-regexp,html .hljs-string{color:#50a14f}html .hljs-attr,html .hljs-number,html .hljs-selector-attr,html .hljs-selector-class,html .hljs-selector-pseudo,html .hljs-template-variable,html .hljs-type,html .hljs-variable{color:#986801}html .hljs-bullet,html .hljs-link,html .hljs-meta,html .hljs-selector-id,html .hljs-symbol,html .hljs-title{color:#4078f2}html .hljs-built_in,html .hljs-class .hljs-title,html .hljs-title.class_{color:#c18401}html .hljs-emphasis{font-style:italic}html .hljs-strong{font-weight:700}html .hljs-link{text-decoration:underline}html.dark .markdown-body{color-scheme:dark;--color-prettylights-syntax-comment: #8b949e;--color-prettylights-syntax-constant: #79c0ff;--color-prettylights-syntax-entity: #d2a8ff;--color-prettylights-syntax-storage-modifier-import: #c9d1d9;--color-prettylights-syntax-entity-tag: #7ee787;--color-prettylights-syntax-keyword: #ff7b72;--color-prettylights-syntax-string: #a5d6ff;--color-prettylights-syntax-variable: #ffa657;--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;--color-prettylights-syntax-invalid-illegal-bg: #8e1519;--color-prettylights-syntax-carriage-return-text: #f0f6fc;--color-prettylights-syntax-carriage-return-bg: #b62324;--color-prettylights-syntax-string-regexp: #7ee787;--color-prettylights-syntax-markup-list: #f2cc60;--color-prettylights-syntax-markup-heading: #1f6feb;--color-prettylights-syntax-markup-italic: #c9d1d9;--color-prettylights-syntax-markup-bold: #c9d1d9;--color-prettylights-syntax-markup-deleted-text: #ffdcd7;--color-prettylights-syntax-markup-deleted-bg: #67060c;--color-prettylights-syntax-markup-inserted-text: #aff5b4;--color-prettylights-syntax-markup-inserted-bg: #033a16;--color-prettylights-syntax-markup-changed-text: #ffdfb6;--color-prettylights-syntax-markup-changed-bg: #5a1e02;--color-prettylights-syntax-markup-ignored-text: #c9d1d9;--color-prettylights-syntax-markup-ignored-bg: #1158c7;--color-prettylights-syntax-meta-diff-range: #d2a8ff;--color-prettylights-syntax-brackethighlighter-angle: #8b949e;--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;--color-fg-default: #c9d1d9;--color-fg-muted: #8b949e;--color-fg-subtle: #6e7681;--color-canvas-default: #0d1117;--color-canvas-subtle: #161b22;--color-border-default: #30363d;--color-border-muted: #21262d;--color-neutral-muted: rgba(110,118,129,.4);--color-accent-fg: #58a6ff;--color-accent-emphasis: #1f6feb;--color-attention-subtle: rgba(187,128,9,.15);--color-danger-fg: #f85149}html .markdown-body{color-scheme:light;--color-prettylights-syntax-comment: #6e7781;--color-prettylights-syntax-constant: #0550ae;--color-prettylights-syntax-entity: #8250df;--color-prettylights-syntax-storage-modifier-import: #24292f;--color-prettylights-syntax-entity-tag: #116329;--color-prettylights-syntax-keyword: #cf222e;--color-prettylights-syntax-string: #0a3069;--color-prettylights-syntax-variable: #953800;--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;--color-prettylights-syntax-invalid-illegal-bg: #82071e;--color-prettylights-syntax-carriage-return-text: #f6f8fa;--color-prettylights-syntax-carriage-return-bg: #cf222e;--color-prettylights-syntax-string-regexp: #116329;--color-prettylights-syntax-markup-list: #3b2300;--color-prettylights-syntax-markup-heading: #0550ae;--color-prettylights-syntax-markup-italic: #24292f;--color-prettylights-syntax-markup-bold: #24292f;--color-prettylights-syntax-markup-deleted-text: #82071e;--color-prettylights-syntax-markup-deleted-bg: #ffebe9;--color-prettylights-syntax-markup-inserted-text: #116329;--color-prettylights-syntax-markup-inserted-bg: #dafbe1;--color-prettylights-syntax-markup-changed-text: #953800;--color-prettylights-syntax-markup-changed-bg: #ffd8b5;--color-prettylights-syntax-markup-ignored-text: #eaeef2;--color-prettylights-syntax-markup-ignored-bg: #0550ae;--color-prettylights-syntax-meta-diff-range: #8250df;--color-prettylights-syntax-brackethighlighter-angle: #57606a;--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;--color-prettylights-syntax-constant-other-reference-link: #0a3069;--color-fg-default: #24292f;--color-fg-muted: #57606a;--color-fg-subtle: #6e7781;--color-canvas-default: #ffffff;--color-canvas-subtle: #f6f8fa;--color-border-default: #d0d7de;--color-border-muted: hsla(210,18%,87%,1);--color-neutral-muted: rgba(175,184,193,.2);--color-accent-fg: #0969da;--color-accent-emphasis: #0969da;--color-attention-subtle: #fff8c5;--color-danger-fg: #cf222e}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;color:var(--color-fg-default);background-color:var(--color-canvas-default);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body h1:hover .anchor .octicon-link:before,.markdown-body h2:hover .anchor .octicon-link:before,.markdown-body h3:hover .anchor .octicon-link:before,.markdown-body h4:hover .anchor .octicon-link:before,.markdown-body h5:hover .anchor .octicon-link:before,.markdown-body h6:hover .anchor .octicon-link:before{width:16px;height:16px;content:" ";display:inline-block;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,
");mask-image:url("data:image/svg+xml,
")}.markdown-body details,.markdown-body figcaption,.markdown-body figure{display:block}.markdown-body summary{display:list-item}.markdown-body [hidden]{display:none!important}.markdown-body a{background-color:transparent;color:var(--color-accent-fg);text-decoration:none}.markdown-body abbr[title]{border-bottom:none;text-decoration:underline dotted}.markdown-body b,.markdown-body strong{font-weight:var(--base-text-weight-semibold, 600)}.markdown-body dfn{font-style:italic}.markdown-body h1{margin:.67em 0;font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--color-border-muted)}.markdown-body mark{background-color:var(--color-attention-subtle);color:var(--color-fg-default)}.markdown-body small{font-size:90%}.markdown-body sub,.markdown-body sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.markdown-body sub{bottom:-.25em}.markdown-body sup{top:-.5em}.markdown-body img{border-style:none;max-width:100%;box-sizing:content-box;background-color:var(--color-canvas-default)}.markdown-body code,.markdown-body kbd,.markdown-body pre,.markdown-body samp{font-family:monospace;font-size:1em}.markdown-body figure{margin:1em 40px}.markdown-body hr{box-sizing:content-box;overflow:hidden;background:transparent;border-bottom:1px solid var(--color-border-muted);height:.25em;padding:0;margin:24px 0;background-color:var(--color-border-default);border:0}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=button],.markdown-body [type=reset],.markdown-body [type=submit]{-webkit-appearance:button}.markdown-body [type=checkbox],.markdown-body [type=radio]{box-sizing:border-box;padding:0}.markdown-body [type=number]::-webkit-inner-spin-button,.markdown-body [type=number]::-webkit-outer-spin-button{height:auto}.markdown-body [type=search]::-webkit-search-cancel-button,.markdown-body [type=search]::-webkit-search-decoration{-webkit-appearance:none}.markdown-body ::-webkit-input-placeholder{color:inherit;opacity:.54}.markdown-body ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.markdown-body a:hover{text-decoration:underline}.markdown-body ::placeholder{color:var(--color-fg-subtle);opacity:1}.markdown-body hr:before{display:table;content:""}.markdown-body hr:after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>*:not(summary){display:none!important}.markdown-body a:focus,.markdown-body [role=button]:focus,.markdown-body input[type=radio]:focus,.markdown-body input[type=checkbox]:focus{outline:2px solid var(--color-accent-fg);outline-offset:-2px;box-shadow:none}.markdown-body a:focus:not(:focus-visible),.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body input[type=radio]:focus:not(:focus-visible),.markdown-body input[type=checkbox]:focus:not(:focus-visible){outline:solid 1px transparent}.markdown-body a:focus-visible,.markdown-body [role=button]:focus-visible,.markdown-body input[type=radio]:focus-visible,.markdown-body input[type=checkbox]:focus-visible{outline:2px solid var(--color-accent-fg);outline-offset:-2px;box-shadow:none}.markdown-body a:not([class]):focus,.markdown-body a:not([class]):focus-visible,.markdown-body input[type=radio]:focus,.markdown-body input[type=radio]:focus-visible,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=checkbox]:focus-visible{outline-offset:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;line-height:10px;color:var(--color-fg-default);vertical-align:middle;background-color:var(--color-canvas-subtle);border:solid 1px var(--color-neutral-muted);border-bottom-color:var(--color-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--color-neutral-muted)}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:var(--base-text-weight-semibold, 600);line-height:1.25}.markdown-body h2{font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--color-border-muted)}.markdown-body h3{font-weight:var(--base-text-weight-semibold, 600);font-size:1.25em}.markdown-body h4{font-weight:var(--base-text-weight-semibold, 600);font-size:1em}.markdown-body h5{font-weight:var(--base-text-weight-semibold, 600);font-size:.875em}.markdown-body h6{font-weight:var(--base-text-weight-semibold, 600);font-size:.85em;color:var(--color-fg-muted)}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0;padding:0 1em;color:var(--color-fg-muted);border-left:.25em solid var(--color-border-default)}.markdown-body ul,.markdown-body ol{margin-top:0;margin-bottom:0;padding-left:2em}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ul ul ol,.markdown-body ul ol ol,.markdown-body ol ul ol,.markdown-body ol ol ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body tt,.markdown-body code,.markdown-body samp{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px}.markdown-body pre{margin-top:0;margin-bottom:0;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px;word-wrap:normal}.markdown-body .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}.markdown-body input::-webkit-outer-spin-button,.markdown-body input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.markdown-body:before{display:table;content:""}.markdown-body:after{display:table;clear:both;content:""}.markdown-body>*:first-child{margin-top:0!important}.markdown-body>*:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:var(--color-danger-fg)}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre,.markdown-body details{margin-top:0;margin-bottom:16px}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:var(--color-fg-default);vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 tt,.markdown-body h1 code,.markdown-body h2 tt,.markdown-body h2 code,.markdown-body h3 tt,.markdown-body h3 code,.markdown-body h4 tt,.markdown-body h4 code,.markdown-body h5 tt,.markdown-body h5 code,.markdown-body h6 tt,.markdown-body h6 code{padding:0 .2em;font-size:inherit}.markdown-body summary h1,.markdown-body summary h2,.markdown-body summary h3,.markdown-body summary h4,.markdown-body summary h5,.markdown-body summary h6{display:inline-block}.markdown-body summary h1 .anchor,.markdown-body summary h2 .anchor,.markdown-body summary h3 .anchor,.markdown-body summary h4 .anchor,.markdown-body summary h5 .anchor,.markdown-body summary h6 .anchor{margin-left:-40px}.markdown-body summary h1,.markdown-body summary h2{padding-bottom:0;border-bottom:0}.markdown-body ul.no-list,.markdown-body ol.no-list{padding:0;list-style-type:none}.markdown-body ol[type=a]{list-style-type:lower-alpha}.markdown-body ol[type=A]{list-style-type:upper-alpha}.markdown-body ol[type=i]{list-style-type:lower-roman}.markdown-body ol[type=I]{list-style-type:upper-roman}.markdown-body ol[type="1"]{list-style-type:decimal}.markdown-body div>ol:not([type]){list-style-type:decimal}.markdown-body ul ul,.markdown-body ul ol,.markdown-body ol ol,.markdown-body ol ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold, 600)}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table th{font-weight:var(--base-text-weight-semibold, 600)}.markdown-body table th,.markdown-body table td{padding:6px 13px;border:1px solid var(--color-border-default)}.markdown-body table tr{background-color:var(--color-canvas-default);border-top:1px solid var(--color-border-muted)}.markdown-body table tr:nth-child(2n){background-color:var(--color-canvas-subtle)}.markdown-body table img{background-color:transparent}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid var(--color-border-default)}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:var(--color-fg-default)}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--color-neutral-muted);border-radius:6px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body samp{font-size:85%}.markdown-body pre code{font-size:100%}.markdown-body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:var(--color-canvas-subtle);border-radius:6px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:var(--color-canvas-default);border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:var(--base-text-weight-semibold, 600);background:var(--color-canvas-subtle);border-top:0}.markdown-body [data-footnote-ref]:before{content:"["}.markdown-body [data-footnote-ref]:after{content:"]"}.markdown-body .footnotes{font-size:12px;color:var(--color-fg-muted);border-top:1px solid var(--color-border-default)}.markdown-body .footnotes ol{padding-left:16px}.markdown-body .footnotes ol ul{display:inline-block;padding-left:16px;margin-top:16px}.markdown-body .footnotes li{position:relative}.markdown-body .footnotes li:target:before{position:absolute;top:-8px;right:-8px;bottom:-8px;left:-24px;pointer-events:none;content:"";border:2px solid var(--color-accent-emphasis);border-radius:6px}.markdown-body .footnotes li:target{color:var(--color-fg-default)}.markdown-body .footnotes .data-footnote-backref g-emoji{font-family:monospace}.markdown-body .pl-c{color:var(--color-prettylights-syntax-comment)}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}.markdown-body .pl-e,.markdown-body .pl-en{color:var(--color-prettylights-syntax-entity)}.markdown-body .pl-smi,.markdown-body .pl-s .pl-s1{color:var(--color-prettylights-syntax-storage-modifier-import)}.markdown-body .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}.markdown-body .pl-k{color:var(--color-prettylights-syntax-keyword)}.markdown-body .pl-s,.markdown-body .pl-pds,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sre,.markdown-body .pl-sr .pl-sra{color:var(--color-prettylights-syntax-string)}.markdown-body .pl-v,.markdown-body .pl-smw{color:var(--color-prettylights-syntax-variable)}.markdown-body .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}.markdown-body .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}.markdown-body .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}.markdown-body .pl-sr .pl-cce{font-weight:700;color:var(--color-prettylights-syntax-string-regexp)}.markdown-body .pl-ml{color:var(--color-prettylights-syntax-markup-list)}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}.markdown-body .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}.markdown-body .pl-mb{font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}.markdown-body .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}.markdown-body .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}.markdown-body .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}.markdown-body .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}.markdown-body .pl-mdr{font-weight:700;color:var(--color-prettylights-syntax-meta-diff-range)}.markdown-body .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}.markdown-body .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}.markdown-body .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}.markdown-body g-emoji{display:inline-block;min-width:1ch;font-family:"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol;font-size:1em;font-style:normal!important;font-weight:var(--base-text-weight-normal, 400);line-height:1;vertical-align:-.075em}.markdown-body g-emoji img{width:1em;height:1em}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item label{font-weight:var(--base-text-weight-normal, 400)}.markdown-body .task-list-item.enabled label{cursor:pointer}.markdown-body .task-list-item+.task-list-item{margin-top:4px}.markdown-body .task-list-item .handle{display:none}.markdown-body .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body .contains-task-list{position:relative}.markdown-body .contains-task-list:hover .task-list-item-convert-container,.markdown-body .contains-task-list:focus-within .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip:auto}.markdown-body ::-webkit-calendar-picker-indicator{filter:invert(50%)}.markdown-body{background-color:transparent;font-size:14px}.markdown-body p{white-space:pre-wrap}.markdown-body ol{list-style-type:decimal}.markdown-body ul{list-style-type:disc}.markdown-body pre code,.markdown-body pre tt{line-height:1.65}.markdown-body code.hljs{padding:0}.markdown-body .code-block-wrapper{position:relative;padding-top:24px}.markdown-body .code-block-header{position:absolute;top:5px;right:0;width:100%;padding:0 1rem;display:flex;justify-content:flex-end;align-items:center;color:#b3b3b3}.markdown-body .code-block-header__copy{cursor:pointer;margin-left:.5rem;-webkit-user-select:none;user-select:none}.markdown-body .code-block-header__copy:hover{color:#65a665}html.dark .message-reply .whitespace-pre-wrap{white-space:pre-wrap;color:var(--n-text-color)}html.dark .highlight pre,html.dark pre{background-color:#282c34}.markdown-body{box-sizing:border-box;background:none}.markdown-body *{font-size:.9rem}@media (max-width: 767px){.markdown-body{padding:15px}}.chat-container{display:flex;flex-direction:column;margin:auto;max-width:1080px;padding:0 5px;height:100%}.chat-messages{flex-grow:1;overflow-y:auto;border-radius:10px 10px 0 0;padding:8px}.chat-message{display:flex;flex-direction:column;margin-bottom:30px}.chat-message-bubble{padding:8px;align-self:flex-start;color:#2f4f4f;border-radius:8px 8px 8px 0;background:linear-gradient(to right,#c9d6ff,#e2e2e2);box-shadow:0 16px 20px #aea7df0f;max-width:80%}.chat-toolbar-icon{margin-left:5px;cursor:pointer}.chat-message-meta{display:flex;position:relative;margin-top:4px}.n-input-wrapper{padding-right:0}.chat-message-nickname-and-time{color:#999;font-size:12px}.is-me{justify-content:flex-end}.n-input .n-input__suffix,.n-input .n-input__prefix{display:block}.n-input .n-input-wrapper{padding-right:0}.n-input .n-input__suffix .n-button{border-radius:8px!important;width:80px;height:30px;position:absolute;right:10px;bottom:10px;box-shadow:inset 0 1px 3px #0000004d;background-image:linear-gradient(-56deg,#0773ff 5%,#797eff);--n-border: none !important}.chat-message.is-me .chat-message-bubble{background:linear-gradient(to right,#56ccf2,#2f80ed);border-radius:8px 8px 0!important;box-shadow:0 16px 20px #aea7df0f;align-self:flex-end}
diff --git a/themes/2023/assets/CardTools.vue_vue_type_script_setup_true_lang-CdJKhv07.js b/themes/2023/assets/CardTools.vue_vue_type_script_setup_true_lang-CdJKhv07.js
deleted file mode 100644
index 0b360b448..000000000
--- a/themes/2023/assets/CardTools.vue_vue_type_script_setup_true_lang-CdJKhv07.js
+++ /dev/null
@@ -1,297 +0,0 @@
-import{e as lg,a as cg,j as ug,b as dg}from"./el-input-CNNwOXGP.js";import{u as _g,a as mg}from"./index-CBpXPsVN.js";import{b as bm,_ as hm,a as gn,aG as pg,c as St,s as pn,u as Tm,ao as gg,aH as Eg,g as Dt,o as Ne,w as ze,B as ot,T as fg,j as X,A as Sg,N as it,aC as bg,O as hg,P as rt,Q as Tn,h as bt,r as Yn,R as pt,W as Ol,as as Cm,C as Tg,l as Rm,d as ka,aI as Cg,aq as Rg,aJ as Ng,ar as yg,a2 as Og,G as _u,n as Bn,k as Ag,aK as Nm,t as mu,z as Ft,aL as ym,a0 as vg,ab as Cl,a8 as Ig,a9 as Om,U as mn,ac as pu,aF as Dg,aa as xg,aM as Mg,ad as wg,aN as Lg,aO as kg,ae as Pg}from"./index-CxMsK_Ni.js";import{e as Fg,f as Bg,g as Ug,E as Gg,h as Yg,d as qg}from"./config-CZWWa62X.js";import{E as zg}from"./el-tag-5pOVEKUY.js";var Hg=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Al(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}const Vg=bm({...Bg,direction:{type:String,default:"rtl",values:["ltr","rtl","ttb","btt"]},size:{type:[String,Number],default:"30%"},withHeader:{type:Boolean,default:!0},modalFade:{type:Boolean,default:!0},headerAriaLevel:{type:String,default:"2"}}),Wg=Fg,$g=gn({name:"ElDrawer",inheritAttrs:!1}),Kg=gn({...$g,props:Vg,emits:Wg,setup(t,{expose:e}){const a=t,o=pg();lg({scope:"el-drawer",from:"the title slot",replacement:"the header slot",version:"3.0.0",ref:"https://element-plus.org/en-US/component/drawer.html#slots"},St(()=>!!o.title));const c=pn(),s=pn(),d=Tm("drawer"),{t:u}=gg(),{afterEnter:_,afterLeave:p,beforeLeave:S,visible:h,rendered:y,titleId:O,bodyId:N,zIndex:w,onModalClick:L,onOpenAutoFocus:D,onCloseAutoFocus:U,onFocusoutPrevented:x,onCloseRequested:G,handleClose:F}=Ug(a,c),z=St(()=>a.direction==="rtl"||a.direction==="ltr"),$=St(()=>Eg(a.size));return e({handleClose:F,afterEnter:_,afterLeave:p}),(V,ee)=>(Ne(),Dt(X(ug),{to:V.appendTo,disabled:V.appendTo!=="body"?!1:!V.appendToBody},{default:ze(()=>[ot(fg,{name:X(d).b("fade"),onAfterEnter:X(_),onAfterLeave:X(p),onBeforeLeave:X(S),persisted:""},{default:ze(()=>[Sg(ot(X(Gg),{mask:V.modal,"overlay-class":V.modalClass,"z-index":X(w),onClick:X(L)},{default:ze(()=>[ot(X(cg),{loop:"",trapped:X(h),"focus-trap-el":c.value,"focus-start-el":s.value,onFocusAfterTrapped:X(D),onFocusAfterReleased:X(U),onFocusoutPrevented:X(x),onReleaseRequested:X(G)},{default:ze(()=>[it("div",bg({ref_key:"drawerRef",ref:c,"aria-modal":"true","aria-label":V.title||void 0,"aria-labelledby":V.title?void 0:X(O),"aria-describedby":X(N)},V.$attrs,{class:[X(d).b(),V.direction,X(h)&&"open"],style:X(z)?"width: "+X($):"height: "+X($),role:"dialog",onClick:hg(()=>{},["stop"])}),[it("span",{ref_key:"focusStartRef",ref:s,class:bt(X(d).e("sr-focus")),tabindex:"-1"},null,2),V.withHeader?(Ne(),rt("header",{key:0,class:bt([X(d).e("header"),V.headerClass])},[V.$slots.title?Yn(V.$slots,"title",{key:1},()=>[Tn(" DEPRECATED SLOT ")]):Yn(V.$slots,"header",{key:0,close:X(F),titleId:X(O),titleClass:X(d).e("title")},()=>[V.$slots.title?Tn("v-if",!0):(Ne(),rt("span",{key:0,id:X(O),role:"heading","aria-level":V.headerAriaLevel,class:bt(X(d).e("title"))},pt(V.title),11,["id","aria-level"]))]),V.showClose?(Ne(),rt("button",{key:2,"aria-label":X(u)("el.drawer.close"),class:bt(X(d).e("close-btn")),type:"button",onClick:X(F)},[ot(X(Ol),{class:bt(X(d).e("close"))},{default:ze(()=>[ot(X(Cm))]),_:1},8,["class"])],10,["aria-label","onClick"])):Tn("v-if",!0)],2)):Tn("v-if",!0),X(y)?(Ne(),rt("div",{key:1,id:X(N),class:bt([X(d).e("body"),V.bodyClass])},[Yn(V.$slots,"default")],10,["id"])):Tn("v-if",!0),V.$slots.footer?(Ne(),rt("div",{key:2,class:bt([X(d).e("footer"),V.footerClass])},[Yn(V.$slots,"footer")],2)):Tn("v-if",!0)],16,["aria-label","aria-labelledby","aria-describedby","onClick"])]),_:3},8,["trapped","focus-trap-el","focus-start-el","onFocusAfterTrapped","onFocusAfterReleased","onFocusoutPrevented","onReleaseRequested"])]),_:3},8,["mask","overlay-class","z-index","onClick"]),[[Tg,X(h)]])]),_:3},8,["name","onAfterEnter","onAfterLeave","onBeforeLeave"])]),_:3},8,["to","disabled"]))}});var Qg=hm(Kg,[["__file","drawer.vue"]]);const Xg=Rm(Qg),Zg=bm({type:{type:String,default:"line",values:["line","circle","dashboard"]},percentage:{type:Number,default:0,validator:t=>t>=0&&t<=100},status:{type:String,default:"",values:["","success","exception","warning"]},indeterminate:Boolean,duration:{type:Number,default:3},strokeWidth:{type:Number,default:6},strokeLinecap:{type:ka(String),default:"round"},textInside:Boolean,width:{type:Number,default:126},showText:{type:Boolean,default:!0},color:{type:ka([String,Array,Function]),default:""},striped:Boolean,stripedFlow:Boolean,format:{type:ka(Function),default:t=>`${t}%`}}),Jg=gn({name:"ElProgress"}),jg=gn({...Jg,props:Zg,setup(t){const e=t,a={success:"#13ce66",exception:"#ff4949",warning:"#e6a23c",default:"#20a0ff"},o=Tm("progress"),c=St(()=>{const x={width:`${e.percentage}%`,animationDuration:`${e.duration}s`},G=U(e.percentage);return G.includes("gradient")?x.background=G:x.backgroundColor=G,x}),s=St(()=>(e.strokeWidth/e.width*100).toFixed(1)),d=St(()=>["circle","dashboard"].includes(e.type)?Number.parseInt(`${50-Number.parseFloat(s.value)/2}`,10):0),u=St(()=>{const x=d.value,G=e.type==="dashboard";return`
- M 50 50
- m 0 ${G?"":"-"}${x}
- a ${x} ${x} 0 1 1 0 ${G?"-":""}${x*2}
- a ${x} ${x} 0 1 1 0 ${G?"":"-"}${x*2}
- `}),_=St(()=>2*Math.PI*d.value),p=St(()=>e.type==="dashboard"?.75:1),S=St(()=>`${-1*_.value*(1-p.value)/2}px`),h=St(()=>({strokeDasharray:`${_.value*p.value}px, ${_.value}px`,strokeDashoffset:S.value})),y=St(()=>({strokeDasharray:`${_.value*p.value*(e.percentage/100)}px, ${_.value}px`,strokeDashoffset:S.value,transition:"stroke-dasharray 0.6s ease 0s, stroke 0.6s ease, opacity ease 0.6s"})),O=St(()=>{let x;return e.color?x=U(e.percentage):x=a[e.status]||a.default,x}),N=St(()=>e.status==="warning"?Cg:e.type==="line"?e.status==="success"?Rg:Ng:e.status==="success"?yg:Cm),w=St(()=>e.type==="line"?12+e.strokeWidth*.4:e.width*.111111+2),L=St(()=>e.format(e.percentage));function D(x){const G=100/x.length;return x.map((z,$)=>_u(z)?{color:z,percentage:($+1)*G}:z).sort((z,$)=>z.percentage-$.percentage)}const U=x=>{var G;const{color:F}=e;if(Og(F))return F(x);if(_u(F))return F;{const z=D(F);for(const $ of z)if($.percentage>x)return $.color;return(G=z[z.length-1])==null?void 0:G.color}};return(x,G)=>(Ne(),rt("div",{class:bt([X(o).b(),X(o).m(x.type),X(o).is(x.status),{[X(o).m("without-text")]:!x.showText,[X(o).m("text-inside")]:x.textInside}]),role:"progressbar","aria-valuenow":x.percentage,"aria-valuemin":"0","aria-valuemax":"100"},[x.type==="line"?(Ne(),rt("div",{key:0,class:bt(X(o).b("bar"))},[it("div",{class:bt(X(o).be("bar","outer")),style:Bn({height:`${x.strokeWidth}px`})},[it("div",{class:bt([X(o).be("bar","inner"),{[X(o).bem("bar","inner","indeterminate")]:x.indeterminate},{[X(o).bem("bar","inner","striped")]:x.striped},{[X(o).bem("bar","inner","striped-flow")]:x.stripedFlow}]),style:Bn(X(c))},[(x.showText||x.$slots.default)&&x.textInside?(Ne(),rt("div",{key:0,class:bt(X(o).be("bar","innerText"))},[Yn(x.$slots,"default",{percentage:x.percentage},()=>[it("span",null,pt(X(L)),1)])],2)):Tn("v-if",!0)],6)],6)],2)):(Ne(),rt("div",{key:1,class:bt(X(o).b("circle")),style:Bn({height:`${x.width}px`,width:`${x.width}px`})},[(Ne(),rt("svg",{viewBox:"0 0 100 100"},[it("path",{class:bt(X(o).be("circle","track")),d:X(u),stroke:`var(${X(o).cssVarName("fill-color-light")}, #e5e9f2)`,"stroke-linecap":x.strokeLinecap,"stroke-width":X(s),fill:"none",style:Bn(X(h))},null,14,["d","stroke","stroke-linecap","stroke-width"]),it("path",{class:bt(X(o).be("circle","path")),d:X(u),stroke:X(O),fill:"none",opacity:x.percentage?1:0,"stroke-linecap":x.strokeLinecap,"stroke-width":X(s),style:Bn(X(y))},null,14,["d","stroke","opacity","stroke-linecap","stroke-width"])]))],6)),(x.showText||x.$slots.default)&&!x.textInside?(Ne(),rt("div",{key:2,class:bt(X(o).e("text")),style:Bn({fontSize:`${X(w)}px`})},[Yn(x.$slots,"default",{percentage:x.percentage},()=>[x.status?(Ne(),Dt(X(Ol),{key:1},{default:ze(()=>[(Ne(),Dt(Ag(X(N))))]),_:1})):(Ne(),rt("span",{key:0},pt(X(L)),1))])],6)):Tn("v-if",!0)],10,["aria-valuenow"]))}});var eE=hm(jg,[["__file","progress.vue"]]);const tE=Rm(eE),nE=Nm("fileData",()=>{const t=mu(JSON.parse(localStorage.getItem("receiveData")||"[]")||[]),e=mu(JSON.parse(localStorage.getItem("shareData")||"[]")||[]);function a(){localStorage.setItem("receiveData",JSON.stringify(t)),localStorage.setItem("shareData",JSON.stringify(e))}function o(u){t.unshift(u),a()}function c(u){e.unshift(u),a()}function s(u){t.splice(u,1),a()}function d(u){e.splice(u,1),a()}return{receiveData:t,shareData:e,save:a,addShareData:c,addReceiveData:o,deleteReceiveData:s,deleteShareData:d}}),Am=Nm("fileBox",()=>({showFileBox:pn(!1)}));function vm(t){return t instanceof Map?t.clear=t.delete=t.set=function(){throw new Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=function(){throw new Error("set is read-only")}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach(e=>{const a=t[e],o=typeof a;(o==="object"||o==="function")&&!Object.isFrozen(a)&&vm(a)}),t}class gu{constructor(e){e.data===void 0&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function Im(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function Nn(t,...e){const a=Object.create(null);for(const o in t)a[o]=t[o];return e.forEach(function(o){for(const c in o)a[c]=o[c]}),a}const rE="",Eu=t=>!!t.scope,aE=(t,{prefix:e})=>{if(t.startsWith("language:"))return t.replace("language:","language-");if(t.includes(".")){const a=t.split(".");return[`${e}${a.shift()}`,...a.map((o,c)=>`${o}${"_".repeat(c+1)}`)].join(" ")}return`${e}${t}`};class iE{constructor(e,a){this.buffer="",this.classPrefix=a.classPrefix,e.walk(this)}addText(e){this.buffer+=Im(e)}openNode(e){if(!Eu(e))return;const a=aE(e.scope,{prefix:this.classPrefix});this.span(a)}closeNode(e){Eu(e)&&(this.buffer+=rE)}value(){return this.buffer}span(e){this.buffer+=`
`}}const fu=(t={})=>{const e={children:[]};return Object.assign(e,t),e};class vl{constructor(){this.rootNode=fu(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const a=fu({scope:e});this.add(a),this.stack.push(a)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,a){return typeof a=="string"?e.addText(a):a.children&&(e.openNode(a),a.children.forEach(o=>this._walk(e,o)),e.closeNode(a)),e}static _collapse(e){typeof e!="string"&&e.children&&(e.children.every(a=>typeof a=="string")?e.children=[e.children.join("")]:e.children.forEach(a=>{vl._collapse(a)}))}}class oE extends vl{constructor(e){super(),this.options=e}addText(e){e!==""&&this.add(e)}startScope(e){this.openNode(e)}endScope(){this.closeNode()}__addSublanguage(e,a){const o=e.root;a&&(o.scope=`language:${a}`),this.add(o)}toHTML(){return new iE(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function or(t){return t?typeof t=="string"?t:t.source:null}function Dm(t){return Ln("(?=",t,")")}function sE(t){return Ln("(?:",t,")*")}function lE(t){return Ln("(?:",t,")?")}function Ln(...t){return t.map(a=>or(a)).join("")}function cE(t){const e=t[t.length-1];return typeof e=="object"&&e.constructor===Object?(t.splice(t.length-1,1),e):{}}function Il(...t){return"("+(cE(t).capture?"":"?:")+t.map(o=>or(o)).join("|")+")"}function xm(t){return new RegExp(t.toString()+"|").exec("").length-1}function uE(t,e){const a=t&&t.exec(e);return a&&a.index===0}const dE=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function Dl(t,{joinWith:e}){let a=0;return t.map(o=>{a+=1;const c=a;let s=or(o),d="";for(;s.length>0;){const u=dE.exec(s);if(!u){d+=s;break}d+=s.substring(0,u.index),s=s.substring(u.index+u[0].length),u[0][0]==="\\"&&u[1]?d+="\\"+String(Number(u[1])+c):(d+=u[0],u[0]==="("&&a++)}return d}).map(o=>`(${o})`).join(e)}const _E=/\b\B/,Mm="[a-zA-Z]\\w*",xl="[a-zA-Z_]\\w*",wm="\\b\\d+(\\.\\d+)?",Lm="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",km="\\b(0b[01]+)",mE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",pE=(t={})=>{const e=/^#![ ]*\//;return t.binary&&(t.begin=Ln(e,/.*\b/,t.binary,/\b.*/)),Nn({scope:"meta",begin:e,end:/$/,relevance:0,"on:begin":(a,o)=>{a.index!==0&&o.ignoreMatch()}},t)},sr={begin:"\\\\[\\s\\S]",relevance:0},gE={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[sr]},EE={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[sr]},fE={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},Yr=function(t,e,a={}){const o=Nn({scope:"comment",begin:t,end:e,contains:[]},a);o.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const c=Il("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return o.contains.push({begin:Ln(/[ ]+/,"(",c,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),o},SE=Yr("//","$"),bE=Yr("/\\*","\\*/"),hE=Yr("#","$"),TE={scope:"number",begin:wm,relevance:0},CE={scope:"number",begin:Lm,relevance:0},RE={scope:"number",begin:km,relevance:0},NE={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[sr,{begin:/\[/,end:/\]/,relevance:0,contains:[sr]}]},yE={scope:"title",begin:Mm,relevance:0},OE={scope:"title",begin:xl,relevance:0},AE={begin:"\\.\\s*"+xl,relevance:0},vE=function(t){return Object.assign(t,{"on:begin":(e,a)=>{a.data._beginMatch=e[1]},"on:end":(e,a)=>{a.data._beginMatch!==e[1]&&a.ignoreMatch()}})};var wr=Object.freeze({__proto__:null,APOS_STRING_MODE:gE,BACKSLASH_ESCAPE:sr,BINARY_NUMBER_MODE:RE,BINARY_NUMBER_RE:km,COMMENT:Yr,C_BLOCK_COMMENT_MODE:bE,C_LINE_COMMENT_MODE:SE,C_NUMBER_MODE:CE,C_NUMBER_RE:Lm,END_SAME_AS_BEGIN:vE,HASH_COMMENT_MODE:hE,IDENT_RE:Mm,MATCH_NOTHING_RE:_E,METHOD_GUARD:AE,NUMBER_MODE:TE,NUMBER_RE:wm,PHRASAL_WORDS_MODE:fE,QUOTE_STRING_MODE:EE,REGEXP_MODE:NE,RE_STARTERS_RE:mE,SHEBANG:pE,TITLE_MODE:yE,UNDERSCORE_IDENT_RE:xl,UNDERSCORE_TITLE_MODE:OE});function IE(t,e){t.input[t.index-1]==="."&&e.ignoreMatch()}function DE(t,e){t.className!==void 0&&(t.scope=t.className,delete t.className)}function xE(t,e){e&&t.beginKeywords&&(t.begin="\\b("+t.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",t.__beforeBegin=IE,t.keywords=t.keywords||t.beginKeywords,delete t.beginKeywords,t.relevance===void 0&&(t.relevance=0))}function ME(t,e){Array.isArray(t.illegal)&&(t.illegal=Il(...t.illegal))}function wE(t,e){if(t.match){if(t.begin||t.end)throw new Error("begin & end are not supported with match");t.begin=t.match,delete t.match}}function LE(t,e){t.relevance===void 0&&(t.relevance=1)}const kE=(t,e)=>{if(!t.beforeMatch)return;if(t.starts)throw new Error("beforeMatch cannot be used with starts");const a=Object.assign({},t);Object.keys(t).forEach(o=>{delete t[o]}),t.keywords=a.keywords,t.begin=Ln(a.beforeMatch,Dm(a.begin)),t.starts={relevance:0,contains:[Object.assign(a,{endsParent:!0})]},t.relevance=0,delete a.beforeMatch},PE=["of","and","for","in","not","or","if","then","parent","list","value"],FE="keyword";function Pm(t,e,a=FE){const o=Object.create(null);return typeof t=="string"?c(a,t.split(" ")):Array.isArray(t)?c(a,t):Object.keys(t).forEach(function(s){Object.assign(o,Pm(t[s],e,s))}),o;function c(s,d){e&&(d=d.map(u=>u.toLowerCase())),d.forEach(function(u){const _=u.split("|");o[_[0]]=[s,BE(_[0],_[1])]})}}function BE(t,e){return e?Number(e):UE(t)?0:1}function UE(t){return PE.includes(t.toLowerCase())}const Su={},Mn=t=>{console.error(t)},bu=(t,...e)=>{console.log(`WARN: ${t}`,...e)},Un=(t,e)=>{Su[`${t}/${e}`]||(console.log(`Deprecated as of ${t}. ${e}`),Su[`${t}/${e}`]=!0)},Fr=new Error;function Fm(t,e,{key:a}){let o=0;const c=t[a],s={},d={};for(let u=1;u<=e.length;u++)d[u+o]=c[u],s[u+o]=!0,o+=xm(e[u-1]);t[a]=d,t[a]._emit=s,t[a]._multi=!0}function GE(t){if(Array.isArray(t.begin)){if(t.skip||t.excludeBegin||t.returnBegin)throw Mn("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),Fr;if(typeof t.beginScope!="object"||t.beginScope===null)throw Mn("beginScope must be object"),Fr;Fm(t,t.begin,{key:"beginScope"}),t.begin=Dl(t.begin,{joinWith:""})}}function YE(t){if(Array.isArray(t.end)){if(t.skip||t.excludeEnd||t.returnEnd)throw Mn("skip, excludeEnd, returnEnd not compatible with endScope: {}"),Fr;if(typeof t.endScope!="object"||t.endScope===null)throw Mn("endScope must be object"),Fr;Fm(t,t.end,{key:"endScope"}),t.end=Dl(t.end,{joinWith:""})}}function qE(t){t.scope&&typeof t.scope=="object"&&t.scope!==null&&(t.beginScope=t.scope,delete t.scope)}function zE(t){qE(t),typeof t.beginScope=="string"&&(t.beginScope={_wrap:t.beginScope}),typeof t.endScope=="string"&&(t.endScope={_wrap:t.endScope}),GE(t),YE(t)}function HE(t){function e(d,u){return new RegExp(or(d),"m"+(t.case_insensitive?"i":"")+(t.unicodeRegex?"u":"")+(u?"g":""))}class a{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(u,_){_.position=this.position++,this.matchIndexes[this.matchAt]=_,this.regexes.push([_,u]),this.matchAt+=xm(u)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const u=this.regexes.map(_=>_[1]);this.matcherRe=e(Dl(u,{joinWith:"|"}),!0),this.lastIndex=0}exec(u){this.matcherRe.lastIndex=this.lastIndex;const _=this.matcherRe.exec(u);if(!_)return null;const p=_.findIndex((h,y)=>y>0&&h!==void 0),S=this.matchIndexes[p];return _.splice(0,p),Object.assign(_,S)}}class o{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(u){if(this.multiRegexes[u])return this.multiRegexes[u];const _=new a;return this.rules.slice(u).forEach(([p,S])=>_.addRule(p,S)),_.compile(),this.multiRegexes[u]=_,_}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(u,_){this.rules.push([u,_]),_.type==="begin"&&this.count++}exec(u){const _=this.getMatcher(this.regexIndex);_.lastIndex=this.lastIndex;let p=_.exec(u);if(this.resumingScanAtSamePosition()&&!(p&&p.index===this.lastIndex)){const S=this.getMatcher(0);S.lastIndex=this.lastIndex+1,p=S.exec(u)}return p&&(this.regexIndex+=p.position+1,this.regexIndex===this.count&&this.considerAll()),p}}function c(d){const u=new o;return d.contains.forEach(_=>u.addRule(_.begin,{rule:_,type:"begin"})),d.terminatorEnd&&u.addRule(d.terminatorEnd,{type:"end"}),d.illegal&&u.addRule(d.illegal,{type:"illegal"}),u}function s(d,u){const _=d;if(d.isCompiled)return _;[DE,wE,zE,kE].forEach(S=>S(d,u)),t.compilerExtensions.forEach(S=>S(d,u)),d.__beforeBegin=null,[xE,ME,LE].forEach(S=>S(d,u)),d.isCompiled=!0;let p=null;return typeof d.keywords=="object"&&d.keywords.$pattern&&(d.keywords=Object.assign({},d.keywords),p=d.keywords.$pattern,delete d.keywords.$pattern),p=p||/\w+/,d.keywords&&(d.keywords=Pm(d.keywords,t.case_insensitive)),_.keywordPatternRe=e(p,!0),u&&(d.begin||(d.begin=/\B|\b/),_.beginRe=e(_.begin),!d.end&&!d.endsWithParent&&(d.end=/\B|\b/),d.end&&(_.endRe=e(_.end)),_.terminatorEnd=or(_.end)||"",d.endsWithParent&&u.terminatorEnd&&(_.terminatorEnd+=(d.end?"|":"")+u.terminatorEnd)),d.illegal&&(_.illegalRe=e(d.illegal)),d.contains||(d.contains=[]),d.contains=[].concat(...d.contains.map(function(S){return VE(S==="self"?d:S)})),d.contains.forEach(function(S){s(S,_)}),d.starts&&s(d.starts,u),_.matcher=c(_),_}if(t.compilerExtensions||(t.compilerExtensions=[]),t.contains&&t.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return t.classNameAliases=Nn(t.classNameAliases||{}),s(t)}function Bm(t){return t?t.endsWithParent||Bm(t.starts):!1}function VE(t){return t.variants&&!t.cachedVariants&&(t.cachedVariants=t.variants.map(function(e){return Nn(t,{variants:null},e)})),t.cachedVariants?t.cachedVariants:Bm(t)?Nn(t,{starts:t.starts?Nn(t.starts):null}):Object.isFrozen(t)?Nn(t):t}var WE="11.11.1";class $E extends Error{constructor(e,a){super(e),this.name="HTMLInjectionError",this.html=a}}const Pa=Im,hu=Nn,Tu=Symbol("nomatch"),KE=7,Um=function(t){const e=Object.create(null),a=Object.create(null),o=[];let c=!0;const s="Could not find the language '{}', did you forget to load/include a language module?",d={disableAutodetect:!0,name:"Plain text",contains:[]};let u={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:oE};function _(W){return u.noHighlightRe.test(W)}function p(W){let te=W.className+" ";te+=W.parentNode?W.parentNode.className:"";const ue=u.languageDetectRe.exec(te);if(ue){const he=V(ue[1]);return he||(bu(s.replace("{}",ue[1])),bu("Falling back to no-highlight mode for this block.",W)),he?ue[1]:"no-highlight"}return te.split(/\s+/).find(he=>_(he)||V(he))}function S(W,te,ue){let he="",Ae="";typeof te=="object"?(he=W,ue=te.ignoreIllegals,Ae=te.language):(Un("10.7.0","highlight(lang, code, ...args) has been deprecated."),Un("10.7.0",`Please use highlight(code, options) instead.
-https://github.com/highlightjs/highlight.js/issues/2277`),Ae=W,he=te),ue===void 0&&(ue=!0);const re={code:he,language:Ae};be("before:highlight",re);const Xe=re.result?re.result:h(re.language,re.code,ue);return Xe.code=re.code,be("after:highlight",Xe),Xe}function h(W,te,ue,he){const Ae=Object.create(null);function re(j,se){return j.keywords[se]}function Xe(){if(!de.keywords){Ve.addText(ye);return}let j=0;de.keywordPatternRe.lastIndex=0;let se=de.keywordPatternRe.exec(ye),Ee="";for(;se;){Ee+=ye.substring(j,se.index);const Se=ht.case_insensitive?se[0].toLowerCase():se[0],Ze=re(de,Se);if(Ze){const[Et,An]=Ze;if(Ve.addText(Ee),Ee="",Ae[Se]=(Ae[Se]||0)+1,Ae[Se]<=KE&&(ln+=An),Et.startsWith("_"))Ee+=se[0];else{const vn=ht.classNameAliases[Et]||Et;Me(se[0],vn)}}else Ee+=se[0];j=de.keywordPatternRe.lastIndex,se=de.keywordPatternRe.exec(ye)}Ee+=ye.substring(j),Ve.addText(Ee)}function st(){if(ye==="")return;let j=null;if(typeof de.subLanguage=="string"){if(!e[de.subLanguage]){Ve.addText(ye);return}j=h(de.subLanguage,ye,!0,gt[de.subLanguage]),gt[de.subLanguage]=j._top}else j=O(ye,de.subLanguage.length?de.subLanguage:null);de.relevance>0&&(ln+=j.relevance),Ve.__addSublanguage(j._emitter,j.language)}function ve(){de.subLanguage!=null?st():Xe(),ye=""}function Me(j,se){j!==""&&(Ve.startScope(se),Ve.addText(j),Ve.endScope())}function Fe(j,se){let Ee=1;const Se=se.length-1;for(;Ee<=Se;){if(!j._emit[Ee]){Ee++;continue}const Ze=ht.classNameAliases[j[Ee]]||j[Ee],Et=se[Ee];Ze?Me(Et,Ze):(ye=Et,Xe(),ye=""),Ee++}}function we(j,se){return j.scope&&typeof j.scope=="string"&&Ve.openNode(ht.classNameAliases[j.scope]||j.scope),j.beginScope&&(j.beginScope._wrap?(Me(ye,ht.classNameAliases[j.beginScope._wrap]||j.beginScope._wrap),ye=""):j.beginScope._multi&&(Fe(j.beginScope,se),ye="")),de=Object.create(j,{parent:{value:de}}),de}function Ie(j,se,Ee){let Se=uE(j.endRe,Ee);if(Se){if(j["on:end"]){const Ze=new gu(j);j["on:end"](se,Ze),Ze.isMatchIgnored&&(Se=!1)}if(Se){for(;j.endsParent&&j.parent;)j=j.parent;return j}}if(j.endsWithParent)return Ie(j.parent,se,Ee)}function Be(j){return de.matcher.regexIndex===0?(ye+=j[0],1):(Ut=!0,0)}function je(j){const se=j[0],Ee=j.rule,Se=new gu(Ee),Ze=[Ee.__beforeBegin,Ee["on:begin"]];for(const Et of Ze)if(Et&&(Et(j,Se),Se.isMatchIgnored))return Be(se);return Ee.skip?ye+=se:(Ee.excludeBegin&&(ye+=se),ve(),!Ee.returnBegin&&!Ee.excludeBegin&&(ye=se)),we(Ee,j),Ee.returnBegin?0:se.length}function et(j){const se=j[0],Ee=te.substring(j.index),Se=Ie(de,j,Ee);if(!Se)return Tu;const Ze=de;de.endScope&&de.endScope._wrap?(ve(),Me(se,de.endScope._wrap)):de.endScope&&de.endScope._multi?(ve(),Fe(de.endScope,j)):Ze.skip?ye+=se:(Ze.returnEnd||Ze.excludeEnd||(ye+=se),ve(),Ze.excludeEnd&&(ye=se));do de.scope&&Ve.closeNode(),!de.skip&&!de.subLanguage&&(ln+=de.relevance),de=de.parent;while(de!==Se.parent);return Se.starts&&we(Se.starts,j),Ze.returnEnd?0:se.length}function ut(){const j=[];for(let se=de;se!==ht;se=se.parent)se.scope&&j.unshift(se.scope);j.forEach(se=>Ve.openNode(se))}let mt={};function En(j,se){const Ee=se&&se[0];if(ye+=j,Ee==null)return ve(),0;if(mt.type==="begin"&&se.type==="end"&&mt.index===se.index&&Ee===""){if(ye+=te.slice(se.index,se.index+1),!c){const Se=new Error(`0 width match regex (${W})`);throw Se.languageName=W,Se.badRule=mt.rule,Se}return 1}if(mt=se,se.type==="begin")return je(se);if(se.type==="illegal"&&!ue){const Se=new Error('Illegal lexeme "'+Ee+'" for mode "'+(de.scope||"")+'"');throw Se.mode=de,Se}else if(se.type==="end"){const Se=et(se);if(Se!==Tu)return Se}if(se.type==="illegal"&&Ee==="")return ye+=`
-`,1;if(Ht>1e5&&Ht>se.index*3)throw new Error("potential infinite loop, way more iterations than matches");return ye+=Ee,Ee.length}const ht=V(W);if(!ht)throw Mn(s.replace("{}",W)),new Error('Unknown language: "'+W+'"');const on=HE(ht);let sn="",de=he||on;const gt={},Ve=new u.__emitter(u);ut();let ye="",ln=0,Rt=0,Ht=0,Ut=!1;try{if(ht.__emitTokens)ht.__emitTokens(te,Ve);else{for(de.matcher.considerAll();;){Ht++,Ut?Ut=!1:de.matcher.considerAll(),de.matcher.lastIndex=Rt;const j=de.matcher.exec(te);if(!j)break;const se=te.substring(Rt,j.index),Ee=En(se,j);Rt=j.index+Ee}En(te.substring(Rt))}return Ve.finalize(),sn=Ve.toHTML(),{language:W,value:sn,relevance:ln,illegal:!1,_emitter:Ve,_top:de}}catch(j){if(j.message&&j.message.includes("Illegal"))return{language:W,value:Pa(te),illegal:!0,relevance:0,_illegalBy:{message:j.message,index:Rt,context:te.slice(Rt-100,Rt+100),mode:j.mode,resultSoFar:sn},_emitter:Ve};if(c)return{language:W,value:Pa(te),illegal:!1,relevance:0,errorRaised:j,_emitter:Ve,_top:de};throw j}}function y(W){const te={value:Pa(W),illegal:!1,relevance:0,_top:d,_emitter:new u.__emitter(u)};return te._emitter.addText(W),te}function O(W,te){te=te||u.languages||Object.keys(e);const ue=y(W),he=te.filter(V).filter(ce).map(ve=>h(ve,W,!1));he.unshift(ue);const Ae=he.sort((ve,Me)=>{if(ve.relevance!==Me.relevance)return Me.relevance-ve.relevance;if(ve.language&&Me.language){if(V(ve.language).supersetOf===Me.language)return 1;if(V(Me.language).supersetOf===ve.language)return-1}return 0}),[re,Xe]=Ae,st=re;return st.secondBest=Xe,st}function N(W,te,ue){const he=te&&a[te]||ue;W.classList.add("hljs"),W.classList.add(`language-${he}`)}function w(W){let te=null;const ue=p(W);if(_(ue))return;if(be("before:highlightElement",{el:W,language:ue}),W.dataset.highlighted){console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",W);return}if(W.children.length>0&&(u.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(W)),u.throwUnescapedHTML))throw new $E("One of your code blocks includes unescaped HTML.",W.innerHTML);te=W;const he=te.textContent,Ae=ue?S(he,{language:ue,ignoreIllegals:!0}):O(he);W.innerHTML=Ae.value,W.dataset.highlighted="yes",N(W,ue,Ae.language),W.result={language:Ae.language,re:Ae.relevance,relevance:Ae.relevance},Ae.secondBest&&(W.secondBest={language:Ae.secondBest.language,relevance:Ae.secondBest.relevance}),be("after:highlightElement",{el:W,result:Ae,text:he})}function L(W){u=hu(u,W)}const D=()=>{G(),Un("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")};function U(){G(),Un("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")}let x=!1;function G(){function W(){G()}if(document.readyState==="loading"){x||window.addEventListener("DOMContentLoaded",W,!1),x=!0;return}document.querySelectorAll(u.cssSelector).forEach(w)}function F(W,te){let ue=null;try{ue=te(t)}catch(he){if(Mn("Language definition for '{}' could not be registered.".replace("{}",W)),c)Mn(he);else throw he;ue=d}ue.name||(ue.name=W),e[W]=ue,ue.rawDefinition=te.bind(null,t),ue.aliases&&ee(ue.aliases,{languageName:W})}function z(W){delete e[W];for(const te of Object.keys(a))a[te]===W&&delete a[te]}function $(){return Object.keys(e)}function V(W){return W=(W||"").toLowerCase(),e[W]||e[a[W]]}function ee(W,{languageName:te}){typeof W=="string"&&(W=[W]),W.forEach(ue=>{a[ue.toLowerCase()]=te})}function ce(W){const te=V(W);return te&&!te.disableAutodetect}function ne(W){W["before:highlightBlock"]&&!W["before:highlightElement"]&&(W["before:highlightElement"]=te=>{W["before:highlightBlock"](Object.assign({block:te.el},te))}),W["after:highlightBlock"]&&!W["after:highlightElement"]&&(W["after:highlightElement"]=te=>{W["after:highlightBlock"](Object.assign({block:te.el},te))})}function me(W){ne(W),o.push(W)}function pe(W){const te=o.indexOf(W);te!==-1&&o.splice(te,1)}function be(W,te){const ue=W;o.forEach(function(he){he[ue]&&he[ue](te)})}function fe(W){return Un("10.7.0","highlightBlock will be removed entirely in v12.0"),Un("10.7.0","Please use highlightElement now."),w(W)}Object.assign(t,{highlight:S,highlightAuto:O,highlightAll:G,highlightElement:w,highlightBlock:fe,configure:L,initHighlighting:D,initHighlightingOnLoad:U,registerLanguage:F,unregisterLanguage:z,listLanguages:$,getLanguage:V,registerAliases:ee,autoDetection:ce,inherit:hu,addPlugin:me,removePlugin:pe}),t.debugMode=function(){c=!1},t.safeMode=function(){c=!0},t.versionString=WE,t.regex={concat:Ln,lookahead:Dm,either:Il,optional:lE,anyNumberOfTimes:sE};for(const W in wr)typeof wr[W]=="object"&&vm(wr[W]);return Object.assign(t,wr),t},Hn=Um({});Hn.newInstance=()=>Um({});var QE=Hn;Hn.HighlightJS=Hn;Hn.default=Hn;var Fa,Cu;function XE(){if(Cu)return Fa;Cu=1;function t(e){const a="[A-Za-zА-Яа-яёЁ_][A-Za-zА-Яа-яёЁ_0-9]+",s="далее "+"возврат вызватьисключение выполнить для если и из или иначе иначеесли исключение каждого конецесли конецпопытки конеццикла не новый перейти перем по пока попытка прервать продолжить тогда цикл экспорт ",_="загрузитьизфайла "+"вебклиент вместо внешнеесоединение клиент конецобласти мобильноеприложениеклиент мобильноеприложениесервер наклиенте наклиентенасервере наклиентенасерверебезконтекста насервере насерверебезконтекста область перед после сервер толстыйклиентобычноеприложение толстыйклиентуправляемоеприложение тонкийклиент ",O="разделительстраниц разделительстрок символтабуляции "+"ansitooem oemtoansi ввестивидсубконто ввестиперечисление ввестипериод ввестиплансчетов выбранныйплансчетов датагод датамесяц датачисло заголовоксистемы значениевстроку значениеизстроки каталогиб каталогпользователя кодсимв конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лог лог10 максимальноеколичествосубконто названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найтиссылки началопериодаби началостандартногоинтервала начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода обработкаожидания основнойжурналрасчетов основнойплансчетов основнойязык очиститьокносообщений периодстр получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта префиксавтонумерации пропись пустоезначение разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо симв создатьобъект статусвозврата стрколичествострок сформироватьпозициюдокумента счетпокоду текущеевремя типзначения типзначениястр установитьтана установитьтапо фиксшаблон шаблон "+"acos asin atan base64значение base64строка cos exp log log10 pow sin sqrt tan xmlзначение xmlстрока xmlтип xmlтипзнч активноеокно безопасныйрежим безопасныйрежимразделенияданных булево ввестидату ввестизначение ввестистроку ввестичисло возможностьчтенияxml вопрос восстановитьзначение врег выгрузитьжурналрегистрации выполнитьобработкуоповещения выполнитьпроверкуправдоступа вычислить год данныеформывзначение дата день деньгода деньнедели добавитьмесяц заблокироватьданныедляредактирования заблокироватьработупользователя завершитьработусистемы загрузитьвнешнююкомпоненту закрытьсправку записатьjson записатьxml записатьдатуjson записьжурналарегистрации заполнитьзначениясвойств запроситьразрешениепользователя запуститьприложение запуститьсистему зафиксироватьтранзакцию значениевданныеформы значениевстрокувнутр значениевфайл значениезаполнено значениеизстрокивнутр значениеизфайла изxmlтипа импортмоделиxdto имякомпьютера имяпользователя инициализироватьпредопределенныеданные информацияобошибке каталогбиблиотекимобильногоустройства каталогвременныхфайлов каталогдокументов каталогпрограммы кодироватьстроку кодлокализацииинформационнойбазы кодсимвола командасистемы конецгода конецдня конецквартала конецмесяца конецминуты конецнедели конецчаса конфигурациябазыданныхизмененадинамически конфигурацияизменена копироватьданныеформы копироватьфайл краткоепредставлениеошибки лев макс местноевремя месяц мин минута монопольныйрежим найти найтинедопустимыесимволыxml найтиокнопонавигационнойссылке найтипомеченныенаудаление найтипоссылкам найтифайлы началогода началодня началоквартала началомесяца началоминуты началонедели началочаса начатьзапросразрешенияпользователя начатьзапускприложения начатькопированиефайла начатьперемещениефайла начатьподключениевнешнейкомпоненты начатьподключениерасширенияработыскриптографией начатьподключениерасширенияработысфайлами начатьпоискфайлов начатьполучениекаталогавременныхфайлов начатьполучениекаталогадокументов начатьполучениерабочегокаталогаданныхпользователя начатьполучениефайлов начатьпомещениефайла начатьпомещениефайлов начатьсозданиедвоичныхданныхизфайла начатьсозданиекаталога начатьтранзакцию начатьудалениефайлов начатьустановкувнешнейкомпоненты начатьустановкурасширенияработыскриптографией начатьустановкурасширенияработысфайлами неделягода необходимостьзавершениясоединения номерсеансаинформационнойбазы номерсоединенияинформационнойбазы нрег нстр обновитьинтерфейс обновитьнумерациюобъектов обновитьповторноиспользуемыезначения обработкапрерыванияпользователя объединитьфайлы окр описаниеошибки оповестить оповеститьобизменении отключитьобработчикзапросанастроекклиенталицензирования отключитьобработчикожидания отключитьобработчикоповещения открытьзначение открытьиндекссправки открытьсодержаниесправки открытьсправку открытьформу открытьформумодально отменитьтранзакцию очиститьжурналрегистрации очиститьнастройкипользователя очиститьсообщения параметрыдоступа перейтипонавигационнойссылке переместитьфайл подключитьвнешнююкомпоненту подключитьобработчикзапросанастроекклиенталицензирования подключитьобработчикожидания подключитьобработчикоповещения подключитьрасширениеработыскриптографией подключитьрасширениеработысфайлами подробноепредставлениеошибки показатьвводдаты показатьвводзначения показатьвводстроки показатьвводчисла показатьвопрос показатьзначение показатьинформациюобошибке показатьнакарте показатьоповещениепользователя показатьпредупреждение полноеимяпользователя получитьcomобъект получитьxmlтип получитьадреспоместоположению получитьблокировкусеансов получитьвремязавершенияспящегосеанса получитьвремязасыпанияпассивногосеанса получитьвремяожиданияблокировкиданных получитьданныевыбора получитьдополнительныйпараметрклиенталицензирования получитьдопустимыекодылокализации получитьдопустимыечасовыепояса получитьзаголовокклиентскогоприложения получитьзаголовоксистемы получитьзначенияотборажурналарегистрации получитьидентификаторконфигурации получитьизвременногохранилища получитьимявременногофайла получитьимяклиенталицензирования получитьинформациюэкрановклиента получитьиспользованиежурналарегистрации получитьиспользованиесобытияжурналарегистрации получитькраткийзаголовокприложения получитьмакетоформления получитьмаскувсефайлы получитьмаскувсефайлыклиента получитьмаскувсефайлысервера получитьместоположениепоадресу получитьминимальнуюдлинупаролейпользователей получитьнавигационнуюссылку получитьнавигационнуюссылкуинформационнойбазы получитьобновлениеконфигурациибазыданных получитьобновлениепредопределенныхданныхинформационнойбазы получитьобщиймакет получитьобщуюформу получитьокна получитьоперативнуюотметкувремени получитьотключениебезопасногорежима получитьпараметрыфункциональныхопцийинтерфейса получитьполноеимяпредопределенногозначения получитьпредставлениянавигационныхссылок получитьпроверкусложностипаролейпользователей получитьразделительпути получитьразделительпутиклиента получитьразделительпутисервера получитьсеансыинформационнойбазы получитьскоростьклиентскогосоединения получитьсоединенияинформационнойбазы получитьсообщенияпользователю получитьсоответствиеобъектаиформы получитьсоставстандартногоинтерфейсаodata получитьструктурухранениябазыданных получитьтекущийсеансинформационнойбазы получитьфайл получитьфайлы получитьформу получитьфункциональнуюопцию получитьфункциональнуюопциюинтерфейса получитьчасовойпоясинформационнойбазы пользователиос поместитьвовременноехранилище поместитьфайл поместитьфайлы прав праводоступа предопределенноезначение представлениекодалокализации представлениепериода представлениеправа представлениеприложения представлениесобытияжурналарегистрации представлениечасовогопояса предупреждение прекратитьработусистемы привилегированныйрежим продолжитьвызов прочитатьjson прочитатьxml прочитатьдатуjson пустаястрока рабочийкаталогданныхпользователя разблокироватьданныедляредактирования разделитьфайл разорватьсоединениесвнешнимисточникомданных раскодироватьстроку рольдоступна секунда сигнал символ скопироватьжурналрегистрации смещениелетнеговремени смещениестандартноговремени соединитьбуферыдвоичныхданных создатькаталог создатьфабрикуxdto сокрл сокрлп сокрп сообщить состояние сохранитьзначение сохранитьнастройкипользователя сред стрдлина стрзаканчиваетсяна стрзаменить стрнайти стрначинаетсяс строка строкасоединенияинформационнойбазы стрполучитьстроку стрразделить стрсоединить стрсравнить стрчисловхождений стрчислострок стршаблон текущаядата текущаядатасеанса текущаяуниверсальнаядата текущаяуниверсальнаядатавмиллисекундах текущийвариантинтерфейсаклиентскогоприложения текущийвариантосновногошрифтаклиентскогоприложения текущийкодлокализации текущийрежимзапуска текущийязык текущийязыксистемы тип типзнч транзакцияактивна трег удалитьданныеинформационнойбазы удалитьизвременногохранилища удалитьобъекты удалитьфайлы универсальноевремя установитьбезопасныйрежим установитьбезопасныйрежимразделенияданных установитьблокировкусеансов установитьвнешнююкомпоненту установитьвремязавершенияспящегосеанса установитьвремязасыпанияпассивногосеанса установитьвремяожиданияблокировкиданных установитьзаголовокклиентскогоприложения установитьзаголовоксистемы установитьиспользованиежурналарегистрации установитьиспользованиесобытияжурналарегистрации установитькраткийзаголовокприложения установитьминимальнуюдлинупаролейпользователей установитьмонопольныйрежим установитьнастройкиклиенталицензирования установитьобновлениепредопределенныхданныхинформационнойбазы установитьотключениебезопасногорежима установитьпараметрыфункциональныхопцийинтерфейса установитьпривилегированныйрежим установитьпроверкусложностипаролейпользователей установитьрасширениеработыскриптографией установитьрасширениеработысфайлами установитьсоединениесвнешнимисточникомданных установитьсоответствиеобъектаиформы установитьсоставстандартногоинтерфейсаodata установитьчасовойпоясинформационнойбазы установитьчасовойпояссеанса формат цел час часовойпояс часовойпояссеанса число числопрописью этоадресвременногохранилища "+"wsссылки библиотекакартинок библиотекамакетовоформлениякомпоновкиданных библиотекастилей бизнеспроцессы внешниеисточникиданных внешниеобработки внешниеотчеты встроенныепокупки главныйинтерфейс главныйстиль документы доставляемыеуведомления журналыдокументов задачи информацияобинтернетсоединении использованиерабочейдаты историяработыпользователя константы критерииотбора метаданные обработки отображениерекламы отправкадоставляемыхуведомлений отчеты панельзадачос параметрзапуска параметрысеанса перечисления планывидоврасчета планывидовхарактеристик планыобмена планысчетов полнотекстовыйпоиск пользователиинформационнойбазы последовательности проверкавстроенныхпокупок рабочаядата расширенияконфигурации регистрыбухгалтерии регистрынакопления регистрырасчета регистрысведений регламентныезадания сериализаторxdto справочники средствагеопозиционирования средствакриптографии средствамультимедиа средстваотображениярекламы средствапочты средствателефонии фабрикаxdto файловыепотоки фоновыезадания хранилищанастроек хранилищевариантовотчетов хранилищенастроекданныхформ хранилищеобщихнастроек хранилищепользовательскихнастроекдинамическихсписков хранилищепользовательскихнастроекотчетов хранилищесистемныхнастроек ",Ae="webцвета windowsцвета windowsшрифты библиотекакартинок рамкистиля символы цветастиля шрифтыстиля "+"автоматическоесохранениеданныхформывнастройках автонумерациявформе автораздвижениесерий анимациядиаграммы вариантвыравниванияэлементовизаголовков вариантуправлениявысотойтаблицы вертикальнаяпрокруткаформы вертикальноеположение вертикальноеположениеэлемента видгруппыформы виддекорацииформы виддополненияэлементаформы видизмененияданных видкнопкиформы видпереключателя видподписейкдиаграмме видполяформы видфлажка влияниеразмеранапузырекдиаграммы горизонтальноеположение горизонтальноеположениеэлемента группировкаколонок группировкаподчиненныхэлементовформы группыиэлементы действиеперетаскивания дополнительныйрежимотображения допустимыедействияперетаскивания интервалмеждуэлементамиформы использованиевывода использованиеполосыпрокрутки используемоезначениеточкибиржевойдиаграммы историявыборапривводе источникзначенийоситочекдиаграммы источникзначенияразмерапузырькадиаграммы категориягруппыкоманд максимумсерий начальноеотображениедерева начальноеотображениесписка обновлениетекстаредактирования ориентациядендрограммы ориентациядиаграммы ориентацияметокдиаграммы ориентацияметоксводнойдиаграммы ориентацияэлементаформы отображениевдиаграмме отображениевлегендедиаграммы отображениегруппыкнопок отображениезаголовкашкалыдиаграммы отображениезначенийсводнойдиаграммы отображениезначенияизмерительнойдиаграммы отображениеинтерваладиаграммыганта отображениекнопки отображениекнопкивыбора отображениеобсужденийформы отображениеобычнойгруппы отображениеотрицательныхзначенийпузырьковойдиаграммы отображениепанелипоиска отображениеподсказки отображениепредупрежденияприредактировании отображениеразметкиполосырегулирования отображениестраницформы отображениетаблицы отображениетекстазначениядиаграммыганта отображениеуправленияобычнойгруппы отображениефигурыкнопки палитрацветовдиаграммы поведениеобычнойгруппы поддержкамасштабадендрограммы поддержкамасштабадиаграммыганта поддержкамасштабасводнойдиаграммы поисквтаблицепривводе положениезаголовкаэлементаформы положениекартинкикнопкиформы положениекартинкиэлементаграфическойсхемы положениекоманднойпанелиформы положениекоманднойпанелиэлементаформы положениеопорнойточкиотрисовки положениеподписейкдиаграмме положениеподписейшкалызначенийизмерительнойдиаграммы положениесостоянияпросмотра положениестрокипоиска положениетекстасоединительнойлинии положениеуправленияпоиском положениешкалывремени порядокотображенияточекгоризонтальнойгистограммы порядоксерийвлегендедиаграммы размеркартинки расположениезаголовкашкалыдиаграммы растягиваниеповертикалидиаграммыганта режимавтоотображениясостояния режимвводастроктаблицы режимвыборанезаполненного режимвыделениядаты режимвыделениястрокитаблицы режимвыделениятаблицы режимизмененияразмера режимизменениясвязанногозначения режимиспользованиядиалогапечати режимиспользованияпараметракоманды режиммасштабированияпросмотра режимосновногоокнаклиентскогоприложения режимоткрытияокнаформы режимотображениявыделения режимотображениягеографическойсхемы режимотображениязначенийсерии режимотрисовкисеткиграфическойсхемы режимполупрозрачностидиаграммы режимпробеловдиаграммы режимразмещениянастранице режимредактированияколонки режимсглаживаниядиаграммы режимсглаживанияиндикатора режимсписказадач сквозноевыравнивание сохранениеданныхформывнастройках способзаполнениятекстазаголовкашкалыдиаграммы способопределенияограничивающегозначениядиаграммы стандартнаягруппакоманд стандартноеоформление статусоповещенияпользователя стильстрелки типаппроксимациилиниитрендадиаграммы типдиаграммы типединицышкалывремени типимпортасерийслоягеографическойсхемы типлиниигеографическойсхемы типлиниидиаграммы типмаркерагеографическойсхемы типмаркерадиаграммы типобластиоформления типорганизацииисточникаданныхгеографическойсхемы типотображениясериислоягеографическойсхемы типотображенияточечногообъектагеографическойсхемы типотображенияшкалыэлементалегендыгеографическойсхемы типпоискаобъектовгеографическойсхемы типпроекциигеографическойсхемы типразмещенияизмерений типразмещенияреквизитовизмерений типрамкиэлементауправления типсводнойдиаграммы типсвязидиаграммыганта типсоединениязначенийпосериямдиаграммы типсоединенияточекдиаграммы типсоединительнойлинии типстороныэлементаграфическойсхемы типформыотчета типшкалырадарнойдиаграммы факторлиниитрендадиаграммы фигуракнопки фигурыграфическойсхемы фиксациявтаблице форматдняшкалывремени форматкартинки ширинаподчиненныхэлементовформы "+"виддвижениябухгалтерии виддвижениянакопления видпериодарегистрарасчета видсчета видточкимаршрутабизнеспроцесса использованиеагрегатарегистранакопления использованиегруппиэлементов использованиережимапроведения использованиесреза периодичностьагрегатарегистранакопления режимавтовремя режимзаписидокумента режимпроведениядокумента "+"авторегистрацияизменений допустимыйномерсообщения отправкаэлементаданных получениеэлементаданных "+"использованиерасшифровкитабличногодокумента ориентациястраницы положениеитоговколоноксводнойтаблицы положениеитоговстроксводнойтаблицы положениетекстаотносительнокартинки расположениезаголовкагруппировкитабличногодокумента способчтениязначенийтабличногодокумента типдвустороннейпечати типзаполненияобластитабличногодокумента типкурсоровтабличногодокумента типлиниирисункатабличногодокумента типлинииячейкитабличногодокумента типнаправленияпереходатабличногодокумента типотображениявыделениятабличногодокумента типотображениялинийсводнойтаблицы типразмещениятекстатабличногодокумента типрисункатабличногодокумента типсмещениятабличногодокумента типузоратабличногодокумента типфайлатабличногодокумента точностьпечати чередованиерасположениястраниц "+"отображениевремениэлементовпланировщика "+"типфайлаформатированногодокумента "+"обходрезультатазапроса типзаписизапроса "+"видзаполнениярасшифровкипостроителяотчета типдобавленияпредставлений типизмеренияпостроителяотчета типразмещенияитогов "+"доступкфайлу режимдиалогавыборафайла режимоткрытияфайла "+"типизмеренияпостроителязапроса "+"видданныханализа методкластеризации типединицыинтервалавременианализаданных типзаполнениятаблицырезультатаанализаданных типиспользованиячисловыхзначенийанализаданных типисточникаданныхпоискаассоциаций типколонкианализаданныхдереворешений типколонкианализаданныхкластеризация типколонкианализаданныхобщаястатистика типколонкианализаданныхпоискассоциаций типколонкианализаданныхпоискпоследовательностей типколонкимоделипрогноза типмерырасстоянияанализаданных типотсеченияправилассоциации типполяанализаданных типстандартизациианализаданных типупорядочиванияправилассоциациианализаданных типупорядочиванияшаблоновпоследовательностейанализаданных типупрощениядереварешений "+"wsнаправлениепараметра вариантxpathxs вариантзаписидатыjson вариантпростоготипаxs видгруппымоделиxs видфасетаxdto действиепостроителяdom завершенностьпростоготипаxs завершенностьсоставноготипаxs завершенностьсхемыxs запрещенныеподстановкиxs исключениягруппподстановкиxs категорияиспользованияатрибутаxs категорияограниченияидентичностиxs категорияограниченияпространствименxs методнаследованияxs модельсодержимогоxs назначениетипаxml недопустимыеподстановкиxs обработкапробельныхсимволовxs обработкасодержимогоxs ограничениезначенияxs параметрыотбораузловdom переносстрокjson позициявдокументеdom пробельныесимволыxml типатрибутаxml типзначенияjson типканоническогоxml типкомпонентыxs типпроверкиxml типрезультатаdomxpath типузлаdom типузлаxml формаxml формапредставленияxs форматдатыjson экранированиесимволовjson "+"видсравнениякомпоновкиданных действиеобработкирасшифровкикомпоновкиданных направлениесортировкикомпоновкиданных расположениевложенныхэлементоврезультатакомпоновкиданных расположениеитоговкомпоновкиданных расположениегруппировкикомпоновкиданных расположениеполейгруппировкикомпоновкиданных расположениеполякомпоновкиданных расположениереквизитовкомпоновкиданных расположениересурсовкомпоновкиданных типбухгалтерскогоостаткакомпоновкиданных типвыводатекстакомпоновкиданных типгруппировкикомпоновкиданных типгруппыэлементовотборакомпоновкиданных типдополненияпериодакомпоновкиданных типзаголовкаполейкомпоновкиданных типмакетагруппировкикомпоновкиданных типмакетаобластикомпоновкиданных типостаткакомпоновкиданных типпериодакомпоновкиданных типразмещениятекстакомпоновкиданных типсвязинаборовданныхкомпоновкиданных типэлементарезультатакомпоновкиданных расположениелегендыдиаграммыкомпоновкиданных типпримененияотборакомпоновкиданных режимотображенияэлементанастройкикомпоновкиданных режимотображениянастроеккомпоновкиданных состояниеэлементанастройкикомпоновкиданных способвосстановлениянастроеккомпоновкиданных режимкомпоновкирезультата использованиепараметракомпоновкиданных автопозицияресурсовкомпоновкиданных вариантиспользованиягруппировкикомпоновкиданных расположениересурсоввдиаграммекомпоновкиданных фиксациякомпоновкиданных использованиеусловногооформлениякомпоновкиданных "+"важностьинтернетпочтовогосообщения обработкатекстаинтернетпочтовогосообщения способкодированияинтернетпочтовоговложения способкодированиянеasciiсимволовинтернетпочтовогосообщения типтекстапочтовогосообщения протоколинтернетпочты статусразборапочтовогосообщения "+"режимтранзакциизаписижурналарегистрации статустранзакциизаписижурналарегистрации уровеньжурналарегистрации "+"расположениехранилищасертификатовкриптографии режимвключениясертификатовкриптографии режимпроверкисертификатакриптографии типхранилищасертификатовкриптографии "+"кодировкаименфайловвzipфайле методсжатияzip методшифрованияzip режимвосстановленияпутейфайловzip режимобработкиподкаталоговzip режимсохраненияпутейzip уровеньсжатияzip "+"звуковоеоповещение направлениепереходакстроке позициявпотоке порядокбайтов режимблокировкиданных режимуправленияблокировкойданных сервисвстроенныхпокупок состояниефоновогозадания типподписчикадоставляемыхуведомлений уровеньиспользованиязащищенногосоединенияftp "+"направлениепорядкасхемызапроса типдополненияпериодамисхемызапроса типконтрольнойточкисхемызапроса типобъединениясхемызапроса типпараметрадоступнойтаблицысхемызапроса типсоединениясхемызапроса "+"httpметод автоиспользованиеобщегореквизита автопрефиксномеразадачи вариантвстроенногоязыка видиерархии видрегистранакопления видтаблицывнешнегоисточникаданных записьдвиженийприпроведении заполнениепоследовательностей индексирование использованиебазыпланавидоврасчета использованиебыстроговыбора использованиеобщегореквизита использованиеподчинения использованиеполнотекстовогопоиска использованиеразделяемыхданныхобщегореквизита использованиереквизита назначениеиспользованияприложения назначениерасширенияконфигурации направлениепередачи обновлениепредопределенныхданных оперативноепроведение основноепредставлениевидарасчета основноепредставлениевидахарактеристики основноепредставлениезадачи основноепредставлениепланаобмена основноепредставлениесправочника основноепредставлениесчета перемещениеграницыприпроведении периодичностьномерабизнеспроцесса периодичностьномерадокумента периодичностьрегистрарасчета периодичностьрегистрасведений повторноеиспользованиевозвращаемыхзначений полнотекстовыйпоискпривводепостроке принадлежностьобъекта проведение разделениеаутентификацииобщегореквизита разделениеданныхобщегореквизита разделениерасширенийконфигурацииобщегореквизита режимавтонумерацииобъектов режимзаписирегистра режимиспользованиямодальности режимиспользованиясинхронныхвызововрасширенийплатформыивнешнихкомпонент режимповторногоиспользованиясеансов режимполученияданныхвыборапривводепостроке режимсовместимости режимсовместимостиинтерфейса режимуправленияблокировкойданныхпоумолчанию сериикодовпланавидовхарактеристик сериикодовпланасчетов сериикодовсправочника созданиепривводе способвыбора способпоискастрокипривводепостроке способредактирования типданныхтаблицывнешнегоисточникаданных типкодапланавидоврасчета типкодасправочника типмакета типномерабизнеспроцесса типномерадокумента типномеразадачи типформы удалениедвижений "+"важностьпроблемыприменениярасширенияконфигурации вариантинтерфейсаклиентскогоприложения вариантмасштабаформклиентскогоприложения вариантосновногошрифтаклиентскогоприложения вариантстандартногопериода вариантстандартнойдатыначала видграницы видкартинки видотображенияполнотекстовогопоиска видрамки видсравнения видцвета видчисловогозначения видшрифта допустимаядлина допустимыйзнак использованиеbyteordermark использованиеметаданныхполнотекстовогопоиска источникрасширенийконфигурации клавиша кодвозвратадиалога кодировкаxbase кодировкатекста направлениепоиска направлениесортировки обновлениепредопределенныхданных обновлениеприизмененииданных отображениепанелиразделов проверказаполнения режимдиалогавопрос режимзапускаклиентскогоприложения режимокругления режимоткрытияформприложения режимполнотекстовогопоиска скоростьклиентскогосоединения состояниевнешнегоисточникаданных состояниеобновленияконфигурациибазыданных способвыборасертификатаwindows способкодированиястроки статуссообщения типвнешнейкомпоненты типплатформы типповеденияклавишиenter типэлементаинформацииовыполненииобновленияконфигурациибазыданных уровеньизоляциитранзакций хешфункция частидаты",st="comобъект ftpсоединение httpзапрос httpсервисответ httpсоединение wsопределения wsпрокси xbase анализданных аннотацияxs блокировкаданных буфердвоичныхданных включениеxs выражениекомпоновкиданных генераторслучайныхчисел географическаясхема географическиекоординаты графическаясхема группамоделиxs данныерасшифровкикомпоновкиданных двоичныеданные дендрограмма диаграмма диаграммаганта диалогвыборафайла диалогвыборацвета диалогвыборашрифта диалограсписаниярегламентногозадания диалогредактированиястандартногопериода диапазон документdom документhtml документацияxs доставляемоеуведомление записьdom записьfastinfoset записьhtml записьjson записьxml записьzipфайла записьданных записьтекста записьузловdom запрос защищенноесоединениеopenssl значенияполейрасшифровкикомпоновкиданных извлечениетекста импортxs интернетпочта интернетпочтовоесообщение интернетпочтовыйпрофиль интернетпрокси интернетсоединение информациядляприложенияxs использованиеатрибутаxs использованиесобытияжурналарегистрации источникдоступныхнастроеккомпоновкиданных итераторузловdom картинка квалификаторыдаты квалификаторыдвоичныхданных квалификаторыстроки квалификаторычисла компоновщикмакетакомпоновкиданных компоновщикнастроеккомпоновкиданных конструктормакетаоформлениякомпоновкиданных конструкторнастроеккомпоновкиданных конструкторформатнойстроки линия макеткомпоновкиданных макетобластикомпоновкиданных макетоформлениякомпоновкиданных маскаxs менеджеркриптографии наборсхемxml настройкикомпоновкиданных настройкисериализацииjson обработкакартинок обработкарасшифровкикомпоновкиданных обходдереваdom объявлениеатрибутаxs объявлениенотацииxs объявлениеэлементаxs описаниеиспользованиясобытиядоступжурналарегистрации описаниеиспользованиясобытияотказвдоступежурналарегистрации описаниеобработкирасшифровкикомпоновкиданных описаниепередаваемогофайла описаниетипов определениегруппыатрибутовxs определениегруппымоделиxs определениеограниченияидентичностиxs определениепростоготипаxs определениесоставноготипаxs определениетипадокументаdom определенияxpathxs отборкомпоновкиданных пакетотображаемыхдокументов параметрвыбора параметркомпоновкиданных параметрызаписиjson параметрызаписиxml параметрычтенияxml переопределениеxs планировщик полеанализаданных полекомпоновкиданных построительdom построительзапроса построительотчета построительотчетаанализаданных построительсхемxml поток потоквпамяти почта почтовоесообщение преобразованиеxsl преобразованиекканоническомуxml процессорвыводарезультатакомпоновкиданныхвколлекциюзначений процессорвыводарезультатакомпоновкиданныхвтабличныйдокумент процессоркомпоновкиданных разыменовательпространствименdom рамка расписаниерегламентногозадания расширенноеимяxml результатчтенияданных своднаядиаграмма связьпараметравыбора связьпотипу связьпотипукомпоновкиданных сериализаторxdto сертификатклиентаwindows сертификатклиентафайл сертификаткриптографии сертификатыудостоверяющихцентровwindows сертификатыудостоверяющихцентровфайл сжатиеданных системнаяинформация сообщениепользователю сочетаниеклавиш сравнениезначений стандартнаядатаначала стандартныйпериод схемаxml схемакомпоновкиданных табличныйдокумент текстовыйдокумент тестируемоеприложение типданныхxml уникальныйидентификатор фабрикаxdto файл файловыйпоток фасетдлиныxs фасетколичестваразрядовдробнойчастиxs фасетмаксимальноговключающегозначенияxs фасетмаксимальногоисключающегозначенияxs фасетмаксимальнойдлиныxs фасетминимальноговключающегозначенияxs фасетминимальногоисключающегозначенияxs фасетминимальнойдлиныxs фасетобразцаxs фасетобщегоколичестваразрядовxs фасетперечисленияxs фасетпробельныхсимволовxs фильтрузловdom форматированнаястрока форматированныйдокумент фрагментxs хешированиеданных хранилищезначения цвет чтениеfastinfoset чтениеhtml чтениеjson чтениеxml чтениеzipфайла чтениеданных чтениетекста чтениеузловdom шрифт элементрезультатакомпоновкиданных "+"comsafearray деревозначений массив соответствие списокзначений структура таблицазначений фиксированнаяструктура фиксированноесоответствие фиксированныймассив ",ve="null истина ложь неопределено",Me=e.inherit(e.NUMBER_MODE),Fe={className:"string",begin:'"|\\|',end:'"|$',contains:[{begin:'""'}]},we={begin:"'",end:"'",excludeBegin:!0,excludeEnd:!0,contains:[{className:"number",begin:"\\d{4}([\\.\\\\/:-]?\\d{2}){0,5}"}]},Ie={match:/[;()+\-:=,]/,className:"punctuation",relevance:0},Be=e.inherit(e.C_LINE_COMMENT_MODE),je={className:"meta",begin:"#|&",end:"$",keywords:{$pattern:a,keyword:s+_},contains:[Be]},et={className:"symbol",begin:"~",end:";|:",excludeEnd:!0},ut={className:"function",variants:[{begin:"процедура|функция",end:"\\)",keywords:"процедура функция"},{begin:"конецпроцедуры|конецфункции",keywords:"конецпроцедуры конецфункции"}],contains:[{begin:"\\(",end:"\\)",endsParent:!0,contains:[{className:"params",begin:a,end:",",excludeEnd:!0,endsWithParent:!0,keywords:{$pattern:a,keyword:"знач",literal:ve},contains:[Me,Fe,we]},Be]},e.inherit(e.TITLE_MODE,{begin:a})]};return{name:"1C:Enterprise",case_insensitive:!0,keywords:{$pattern:a,keyword:s,built_in:O,class:Ae,type:st,literal:ve},contains:[je,ut,Be,et,Me,Fe,we,Ie]}}return Fa=t,Fa}var Ba,Ru;function ZE(){if(Ru)return Ba;Ru=1;function t(e){const a=e.regex,o=/^[a-zA-Z][a-zA-Z0-9-]*/,c=["ALPHA","BIT","CHAR","CR","CRLF","CTL","DIGIT","DQUOTE","HEXDIG","HTAB","LF","LWSP","OCTET","SP","VCHAR","WSP"],s=e.COMMENT(/;/,/$/),d={scope:"symbol",match:/%b[0-1]+(-[0-1]+|(\.[0-1]+)+)?/},u={scope:"symbol",match:/%d[0-9]+(-[0-9]+|(\.[0-9]+)+)?/},_={scope:"symbol",match:/%x[0-9A-F]+(-[0-9A-F]+|(\.[0-9A-F]+)+)?/},p={scope:"symbol",match:/%[si](?=".*")/},S={scope:"attribute",match:a.concat(o,/(?=\s*=)/)};return{name:"Augmented Backus-Naur Form",illegal:/[!@#$^&',?+~`|:]/,keywords:c,contains:[{scope:"operator",match:/=\/?/},S,s,d,u,_,p,e.QUOTE_STRING_MODE,e.NUMBER_MODE]}}return Ba=t,Ba}var Ua,Nu;function JE(){if(Nu)return Ua;Nu=1;function t(e){const a=e.regex,o=["GET","POST","HEAD","PUT","DELETE","CONNECT","OPTIONS","PATCH","TRACE"];return{name:"Apache Access Log",contains:[{className:"number",begin:/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?\b/,relevance:5},{className:"number",begin:/\b\d+\b/,relevance:0},{className:"string",begin:a.concat(/"/,a.either(...o)),end:/"/,keywords:o,illegal:/\n/,relevance:5,contains:[{begin:/HTTP\/[12]\.\d'/,relevance:5}]},{className:"string",begin:/\[\d[^\]\n]{8,}\]/,illegal:/\n/,relevance:1},{className:"string",begin:/\[/,end:/\]/,illegal:/\n/,relevance:0},{className:"string",begin:/"Mozilla\/\d\.\d \(/,end:/"/,illegal:/\n/,relevance:3},{className:"string",begin:/"/,end:/"/,illegal:/\n/,relevance:0}]}}return Ua=t,Ua}var Ga,yu;function jE(){if(yu)return Ga;yu=1;function t(e){const a=e.regex,o=/[a-zA-Z_$][a-zA-Z0-9_$]*/,c=a.concat(o,a.concat("(\\.",o,")*")),s=/([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)/,d={className:"rest_arg",begin:/[.]{3}/,end:o,relevance:10};return{name:"ActionScript",aliases:["as"],keywords:{keyword:["as","break","case","catch","class","const","continue","default","delete","do","dynamic","each","else","extends","final","finally","for","function","get","if","implements","import","in","include","instanceof","interface","internal","is","namespace","native","new","override","package","private","protected","public","return","set","static","super","switch","this","throw","try","typeof","use","var","void","while","with"],literal:["true","false","null","undefined"]},contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.C_NUMBER_MODE,{match:[/\bpackage/,/\s+/,c],className:{1:"keyword",3:"title.class"}},{match:[/\b(?:class|interface|extends|implements)/,/\s+/,o],className:{1:"keyword",3:"title.class"}},{className:"meta",beginKeywords:"import include",end:/;/,keywords:{keyword:"import include"}},{beginKeywords:"function",end:/[{;]/,excludeEnd:!0,illegal:/\S/,contains:[e.inherit(e.TITLE_MODE,{className:"title.function"}),{className:"params",begin:/\(/,end:/\)/,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,d]},{begin:a.concat(/:\s*/,s)}]},e.METHOD_GUARD],illegal:/#/}}return Ga=t,Ga}var Ya,Ou;function ef(){if(Ou)return Ya;Ou=1;function t(e){const a="\\d(_|\\d)*",o="[eE][-+]?"+a,c=a+"(\\."+a+")?("+o+")?",s="\\w+",u="\\b("+(a+"#"+s+"(\\."+s+")?#("+o+")?")+"|"+c+")",_="[A-Za-z](_?[A-Za-z0-9.])*",p=`[]\\{\\}%#'"`,S=e.COMMENT("--","$"),h={begin:"\\s+:\\s+",end:"\\s*(:=|;|\\)|=>|$)",illegal:p,contains:[{beginKeywords:"loop for declare others",endsParent:!0},{className:"keyword",beginKeywords:"not null constant access function procedure in out aliased exception"},{className:"type",begin:_,endsParent:!0,relevance:0}]};return{name:"Ada",case_insensitive:!0,keywords:{keyword:["abort","else","new","return","abs","elsif","not","reverse","abstract","end","accept","entry","select","access","exception","of","separate","aliased","exit","or","some","all","others","subtype","and","for","out","synchronized","array","function","overriding","at","tagged","generic","package","task","begin","goto","pragma","terminate","body","private","then","if","procedure","type","case","in","protected","constant","interface","is","raise","use","declare","range","delay","limited","record","when","delta","loop","rem","while","digits","renames","with","do","mod","requeue","xor"],literal:["True","False"]},contains:[S,{className:"string",begin:/"/,end:/"/,contains:[{begin:/""/,relevance:0}]},{className:"string",begin:/'.'/},{className:"number",begin:u,relevance:0},{className:"symbol",begin:"'"+_},{className:"title",begin:"(\\bwith\\s+)?(\\bprivate\\s+)?\\bpackage\\s+(\\bbody\\s+)?",end:"(is|$)",keywords:"package body",excludeBegin:!0,excludeEnd:!0,illegal:p},{begin:"(\\b(with|overriding)\\s+)?\\b(function|procedure)\\s+",end:"(\\bis|\\bwith|\\brenames|\\)\\s*;)",keywords:"overriding function procedure with is renames return",returnBegin:!0,contains:[S,{className:"title",begin:"(\\bwith\\s+)?\\b(function|procedure)\\s+",end:"(\\(|\\s+|$)",excludeBegin:!0,excludeEnd:!0,illegal:p},h,{className:"type",begin:"\\breturn\\s+",end:"(\\s+|;|$)",keywords:"return",excludeBegin:!0,excludeEnd:!0,endsParent:!0,illegal:p}]},{className:"type",begin:"\\b(sub)?type\\s+",end:"\\s+",keywords:"type",excludeBegin:!0,illegal:p},h]}}return Ya=t,Ya}var qa,Au;function tf(){if(Au)return qa;Au=1;function t(e){const a={className:"built_in",begin:"\\b(void|bool|int8|int16|int32|int64|int|uint8|uint16|uint32|uint64|uint|string|ref|array|double|float|auto|dictionary)"},o={className:"symbol",begin:"[a-zA-Z0-9_]+@"},c={className:"keyword",begin:"<",end:">",contains:[a,o]};return a.contains=[c],o.contains=[c],{name:"AngelScript",aliases:["asc"],keywords:["for","in|0","break","continue","while","do|0","return","if","else","case","switch","namespace","is","cast","or","and","xor","not","get|0","in","inout|10","out","override","set|0","private","public","const","default|0","final","shared","external","mixin|10","enum","typedef","funcdef","this","super","import","from","interface","abstract|0","try","catch","protected","explicit","property"],illegal:"(^using\\s+[A-Za-z0-9_\\.]+;$|\\bfunction\\s*[^\\(])",contains:[{className:"string",begin:"'",end:"'",illegal:"\\n",contains:[e.BACKSLASH_ESCAPE],relevance:0},{className:"string",begin:'"""',end:'"""'},{className:"string",begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE],relevance:0},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",begin:"^\\s*\\[",end:"\\]"},{beginKeywords:"interface namespace",end:/\{/,illegal:"[;.\\-]",contains:[{className:"symbol",begin:"[a-zA-Z0-9_]+"}]},{beginKeywords:"class",end:/\{/,illegal:"[;.\\-]",contains:[{className:"symbol",begin:"[a-zA-Z0-9_]+",contains:[{begin:"[:,]\\s*",contains:[{className:"symbol",begin:"[a-zA-Z0-9_]+"}]}]}]},a,o,{className:"literal",begin:"\\b(null|true|false)"},{className:"number",relevance:0,begin:"(-?)(\\b0[xXbBoOdD][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?f?|\\.\\d+f?)([eE][-+]?\\d+f?)?)"}]}}return qa=t,qa}var za,vu;function nf(){if(vu)return za;vu=1;function t(e){const a={className:"number",begin:/[$%]\d+/},o={className:"number",begin:/\b\d+/},c={className:"number",begin:/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?/},s={className:"number",begin:/:\d{1,5}/};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:/<\/?/,end:/>/,contains:[c,s,e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{_:["order","deny","allow","setenv","rewriterule","rewriteengine","rewritecond","documentroot","sethandler","errordocument","loadmodule","options","header","listen","serverroot","servername"]},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{scope:"punctuation",match:/\\\n/},{className:"meta",begin:/\s\[/,end:/\]$/},{className:"variable",begin:/[\$%]\{/,end:/\}/,contains:["self",a]},c,o,e.QUOTE_STRING_MODE]}}],illegal:/\S/}}return za=t,za}var Ha,Iu;function rf(){if(Iu)return Ha;Iu=1;function t(e){const a=e.regex,o=e.inherit(e.QUOTE_STRING_MODE,{illegal:null}),c={className:"params",begin:/\(/,end:/\)/,contains:["self",e.C_NUMBER_MODE,o]},s=e.COMMENT(/--/,/$/),d=e.COMMENT(/\(\*/,/\*\)/,{contains:["self",s]}),u=[s,d,e.HASH_COMMENT_MODE],_=[/apart from/,/aside from/,/instead of/,/out of/,/greater than/,/isn't|(doesn't|does not) (equal|come before|come after|contain)/,/(greater|less) than( or equal)?/,/(starts?|ends|begins?) with/,/contained by/,/comes (before|after)/,/a (ref|reference)/,/POSIX (file|path)/,/(date|time) string/,/quoted form/],p=[/clipboard info/,/the clipboard/,/info for/,/list (disks|folder)/,/mount volume/,/path to/,/(close|open for) access/,/(get|set) eof/,/current date/,/do shell script/,/get volume settings/,/random number/,/set volume/,/system attribute/,/system info/,/time to GMT/,/(load|run|store) script/,/scripting components/,/ASCII (character|number)/,/localized string/,/choose (application|color|file|file name|folder|from list|remote application|URL)/,/display (alert|dialog)/];return{name:"AppleScript",aliases:["osascript"],keywords:{keyword:"about above after against and around as at back before beginning behind below beneath beside between but by considering contain contains continue copy div does eighth else end equal equals error every exit fifth first for fourth from front get given global if ignoring in into is it its last local me middle mod my ninth not of on onto or over prop property put ref reference repeat returning script second set seventh since sixth some tell tenth that the|0 then third through thru timeout times to transaction try until where while whose with without",literal:"AppleScript false linefeed return pi quote result space tab true",built_in:"alias application boolean class constant date file integer list number real record string text activate beep count delay launch log offset read round run say summarize write character characters contents day frontmost id item length month name|0 paragraph paragraphs rest reverse running time version weekday word words year"},contains:[o,e.C_NUMBER_MODE,{className:"built_in",begin:a.concat(/\b/,a.either(...p),/\b/)},{className:"built_in",begin:/^\s*return\b/},{className:"literal",begin:/\b(text item delimiters|current application|missing value)\b/},{className:"keyword",begin:a.concat(/\b/,a.either(..._),/\b/)},{beginKeywords:"on",illegal:/[${=;\n]/,contains:[e.UNDERSCORE_TITLE_MODE,c]},...u],illegal:/\/\/|->|=>|\[\[/}}return Ha=t,Ha}var Va,Du;function af(){if(Du)return Va;Du=1;function t(e){const a=e.regex,o="[A-Za-z_][0-9A-Za-z_]*",c={keyword:["break","case","catch","continue","debugger","do","else","export","for","function","if","import","in","new","of","return","switch","try","var","void","while"],literal:["BackSlash","DoubleQuote","ForwardSlash","Infinity","NaN","NewLine","PI","SingleQuote","Tab","TextFormatting","false","null","true","undefined"],built_in:["Abs","Acos","All","Angle","Any","Area","AreaGeodetic","Array","Asin","Atan","Atan2","Attachments","Average","Back","Bearing","Boolean","Buffer","BufferGeodetic","Ceil","Centroid","ChangeTimeZone","Clip","Concatenate","Console","Constrain","Contains","ConvertDirection","ConvexHull","Cos","Count","Crosses","Cut","Date|0","DateAdd","DateDiff","DateOnly","Day","Decode","DefaultValue","Densify","DensifyGeodetic","Dictionary","Difference","Disjoint","Distance","DistanceGeodetic","DistanceToCoordinate","Distinct","Domain","DomainCode","DomainName","EnvelopeIntersects","Equals","Erase","Exp","Expects","Extent","Feature","FeatureInFilter","FeatureSet","FeatureSetByAssociation","FeatureSetById","FeatureSetByName","FeatureSetByPortalItem","FeatureSetByRelationshipClass","FeatureSetByRelationshipName","Filter","FilterBySubtypeCode","Find","First|0","Floor","FromCharCode","FromCodePoint","FromJSON","Front","GdbVersion","Generalize","Geometry","GetEnvironment","GetFeatureSet","GetFeatureSetInfo","GetUser","GroupBy","Guid","HasKey","HasValue","Hash","Hour","IIf","ISOMonth","ISOWeek","ISOWeekday","ISOYear","Includes","IndexOf","Insert","Intersection","Intersects","IsEmpty","IsNan","IsSelfIntersecting","IsSimple","KnowledgeGraphByPortalItem","Left|0","Length","Length3D","LengthGeodetic","Log","Lower","Map","Max","Mean","MeasureToCoordinate","Mid","Millisecond","Min","Minute","Month","MultiPartToSinglePart","Multipoint","NearestCoordinate","NearestVertex","NextSequenceValue","None","Now","Number","Offset","OrderBy","Overlaps","Point","PointToCoordinate","Polygon","Polyline","Pop","Portal","Pow","Proper","Push","QueryGraph","Random","Reduce","Relate","Replace","Resize","Reverse","Right|0","RingIsClockwise","Rotate","Round","Schema","Second","SetGeometry","Simplify","Sin","Slice","Sort","Splice","Split","Sqrt","StandardizeFilename","StandardizeGuid","Stdev","SubtypeCode","SubtypeName","Subtypes","Sum","SymmetricDifference","Tan","Text","Time","TimeZone","TimeZoneOffset","Timestamp","ToCharCode","ToCodePoint","ToHex","ToLocal","ToUTC","Today","Top|0","Touches","TrackAccelerationAt","TrackAccelerationWindow","TrackCurrentAcceleration","TrackCurrentDistance","TrackCurrentSpeed","TrackCurrentTime","TrackDistanceAt","TrackDistanceWindow","TrackDuration","TrackFieldWindow","TrackGeometryWindow","TrackIndex","TrackSpeedAt","TrackSpeedWindow","TrackStartTime","TrackWindow","Trim","TypeOf","Union","Upper","UrlEncode","Variance","Week","Weekday","When|0","Within","Year|0"]},s=["aggregatedFeatures","analytic","config","datapoint","datastore","editcontext","feature","featureSet","feedfeature","fencefeature","fencenotificationtype","graph","join","layer","locationupdate","map","measure","measure","originalFeature","record","reference","rowindex","sourcedatastore","sourcefeature","sourcelayer","target","targetdatastore","targetfeature","targetlayer","userInput","value","variables","view"],d={className:"symbol",begin:"\\$"+a.either(...s)},u={className:"number",variants:[{begin:"\\b(0[bB][01]+)"},{begin:"\\b(0[oO][0-7]+)"},{begin:e.C_NUMBER_RE}],relevance:0},_={className:"subst",begin:"\\$\\{",end:"\\}",keywords:c,contains:[]},p={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,_]};_.contains=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,u,e.REGEXP_MODE];const S=_.contains.concat([e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]);return{name:"ArcGIS Arcade",case_insensitive:!0,keywords:c,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,d,u,{begin:/[{,]\s*/,relevance:0,contains:[{begin:o+"\\s*:",returnBegin:!0,relevance:0,contains:[{className:"attr",begin:o,relevance:0}]}]},{begin:"("+e.RE_STARTERS_RE+"|\\b(return)\\b)\\s*",keywords:"return",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.REGEXP_MODE,{className:"function",begin:"(\\(.*?\\)|"+o+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:o},{begin:/\(\s*\)/},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:c,contains:S}]}]}],relevance:0},{beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{className:"title.function",begin:o}),{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:S}],illegal:/\[|%/},{begin:/\$[(.]/}],illegal:/#(?!!)/}}return Va=t,Va}var Wa,xu;function of(){if(xu)return Wa;xu=1;function t(a){const o=a.regex,c=a.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),s="decltype\\(auto\\)",d="[a-zA-Z_]\\w*::",_="(?!struct)("+s+"|"+o.optional(d)+"[a-zA-Z_]\\w*"+o.optional("<[^<>]+>")+")",p={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},h={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[a.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:"."},a.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},y={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},O={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},a.inherit(h,{className:"string"}),{className:"string",begin:/<.*?>/},c,a.C_BLOCK_COMMENT_MODE]},N={className:"title",begin:o.optional(d)+a.IDENT_RE,relevance:0},w=o.optional(d)+a.IDENT_RE+"\\s*\\(",L=["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"],D=["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],U=["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"],x=["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"],z={type:D,keyword:L,literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:U},$={className:"function.dispatch",relevance:0,keywords:{_hint:x},begin:o.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,a.IDENT_RE,o.lookahead(/(<[^<>]+>|)\s*\(/))},V=[$,O,p,c,a.C_BLOCK_COMMENT_MODE,y,h],ee={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:z,contains:V.concat([{begin:/\(/,end:/\)/,keywords:z,contains:V.concat(["self"]),relevance:0}]),relevance:0},ce={className:"function",begin:"("+_+"[\\*&\\s]+)+"+w,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:z,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:s,keywords:z,relevance:0},{begin:w,returnBegin:!0,contains:[N],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[h,y]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:z,relevance:0,contains:[c,a.C_BLOCK_COMMENT_MODE,h,y,p,{begin:/\(/,end:/\)/,keywords:z,relevance:0,contains:["self",c,a.C_BLOCK_COMMENT_MODE,h,y,p]}]},p,c,a.C_BLOCK_COMMENT_MODE,O]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:z,illegal:"",classNameAliases:{"function.dispatch":"built_in"},contains:[].concat(ee,ce,$,V,[O,{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:z,contains:["self",p]},{begin:a.IDENT_RE+"::",keywords:z},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}}function e(a){const o={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"]},c=t(a),s=c.keywords;return s.type=[...s.type,...o.type],s.literal=[...s.literal,...o.literal],s.built_in=[...s.built_in,...o.built_in],s._hints=o._hints,c.name="Arduino",c.aliases=["ino"],c.supersetOf="cpp",c}return Wa=e,Wa}var $a,Mu;function sf(){if(Mu)return $a;Mu=1;function t(e){const a={variants:[e.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),e.COMMENT("[;@]","$",{relevance:0}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+e.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 w0 w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 w13 w14 w15 w16 w17 w18 w19 w20 w21 w22 w23 w24 w25 w26 w27 w28 w29 w30 x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},a,e.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}return $a=t,$a}var Ka,wu;function lf(){if(wu)return Ka;wu=1;function t(e){const a=e.regex,o=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),c=/[\p{L}0-9._:-]+/u,s={className:"symbol",begin:/&[a-z]+;|[0-9]+;|[a-f0-9]+;/},d={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},u=e.inherit(d,{begin:/\(/,end:/\)/}),_=e.inherit(e.APOS_STRING_MODE,{className:"string"}),p=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),S={endsWithParent:!0,illegal:/,relevance:0,contains:[{className:"attr",begin:c,relevance:0},{begin:/=\s*/,relevance:0,contains:[{className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[s]},{begin:/'/,end:/'/,contains:[s]},{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,p,_,u,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[d,u,p,_]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[p]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/