From 51f621b95a2a4ec97290436187c0ed4dda5101d1 Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 19:13:43 +0800 Subject: [PATCH 1/8] feat: add lightweight astrbot pages entry --- core/plugin_lifecycle.py | 1 + core/web_admin_server.py | 70 ++++++++++ pages/proactive-chat/index.html | 220 +++++++++++++++++++++++++++++++ pages/proactive-chat/page-app.js | 142 ++++++++++++++++++++ 4 files changed, 433 insertions(+) create mode 100644 pages/proactive-chat/index.html create mode 100644 pages/proactive-chat/page-app.js diff --git a/core/plugin_lifecycle.py b/core/plugin_lifecycle.py index cf3c28b..0a90caa 100644 --- a/core/plugin_lifecycle.py +++ b/core/plugin_lifecycle.py @@ -136,6 +136,7 @@ async def initialize(self) -> None: try: if self.web_admin_server: await self.web_admin_server.start() + self.web_admin_server.register_astrbot_page_api() except Exception as e: logger.error(f"[主动消息] Web 管理端启动失败喵: {e}") if self.telemetry and self.telemetry.enabled: diff --git a/core/web_admin_server.py b/core/web_admin_server.py index 0460ddf..c53665e 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -57,6 +57,7 @@ def _is_running_in_docker() -> bool: class WebAdminServer: + ASTRBOT_PLUGIN_NAME = "astrbot_plugin_proactive_chat" """主动消息插件 Web 管理端服务器。""" def __init__(self, plugin: Any): @@ -101,6 +102,75 @@ def __init__(self, plugin: Any): f" 可能是 FastAPI / Pydantic 依赖版本不兼容: {e}" ) + def register_astrbot_page_api(self) -> None: + """Register the lightweight AstrBot Pages API used by the plugin card.""" + context = getattr(self.plugin, "context", None) + register = getattr(context, "register_web_api", None) + if not callable(register): + logger.debug( + "[主动消息] 当前 AstrBot 版本未提供 register_web_api,跳过 Pages 接口注册喵。" + ) + return + + path = f"/{self.ASTRBOT_PLUGIN_NAME}/dashboard" + attempts = ( + lambda: register( + path, + self.build_astrbot_page_payload, + ["GET"], + "Proactive Chat page dashboard", + ), + lambda: register( + path, + self.build_astrbot_page_payload, + methods=["GET"], + ), + lambda: register("GET", path, self.build_astrbot_page_payload), + ) + for attempt in attempts: + try: + attempt() + logger.info("[主动消息] 已注册 AstrBot Pages 管理入口接口喵。") + return + except TypeError: + continue + except Exception as e: + logger.debug(f"[主动消息] 注册 AstrBot Pages 接口失败喵: {e}") + return + + logger.debug( + "[主动消息] 当前 AstrBot register_web_api 签名不兼容,跳过 Pages 接口注册喵。" + ) + + def _build_web_admin_url_payload(self) -> dict[str, Any]: + web_admin = self.config.get("web_admin", {}) + host = str(web_admin.get("host", "127.0.0.1") or "127.0.0.1") + try: + port = int(web_admin.get("port", 4100)) + except (TypeError, ValueError): + port = 4100 + + bind_all = host in {"0.0.0.0", "::", ""} + display_host = "127.0.0.1" if bind_all else host + return { + "enabled": bool(web_admin.get("enabled", True)), + "available": bool(self._web_admin_available), + "running": bool(self.server_task and not self.server_task.done()), + "host": host, + "port": port, + "bind_all": bind_all, + "url": f"http://{display_host}:{port}/", + } + + async def build_astrbot_page_payload(self) -> dict[str, Any]: + """Build a small runtime snapshot for the AstrBot Pages entry.""" + return { + "status": self._build_status_payload(), + "jobs": self._collect_jobs(), + "sessions": self._list_known_session_summaries(), + "web_admin": self._build_web_admin_url_payload(), + } + def _setup_app(self) -> None: # 创建 FastAPI 应用,版本号用于控制台元信息展示。 self.app = FastAPI( diff --git a/pages/proactive-chat/index.html b/pages/proactive-chat/index.html new file mode 100644 index 0000000..e105faa --- /dev/null +++ b/pages/proactive-chat/index.html @@ -0,0 +1,220 @@ + + + + + + 主动消息管理端 + + + +
+
+
+ +
+

主动消息管理端

+
正在连接 AstrBot Pages...
+
+
+
+ + 新窗口打开 +
+
+
+ +
+
正在加载管理端...
+
+
+
+ + + diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js new file mode 100644 index 0000000..a77aa01 --- /dev/null +++ b/pages/proactive-chat/page-app.js @@ -0,0 +1,142 @@ +(function () { + "use strict"; + + var DEFAULT_URL = "http://127.0.0.1:4100/"; + var state = { + bridge: null, + payload: null, + webUrl: DEFAULT_URL + }; + + function byId(id) { + return document.getElementById(id); + } + + function text(id, value) { + var node = byId(id); + if (node) node.textContent = value == null || value === "" ? "--" : String(value); + } + + function normalizePayload(payload) { + if (!payload || typeof payload !== "object") return {}; + if (payload.ok === true && payload.data && typeof payload.data === "object") { + return payload.data; + } + if (payload.success === true && payload.data && typeof payload.data === "object") { + return payload.data; + } + return payload; + } + + function waitBridge(timeoutMs) { + return new Promise(function (resolve) { + var started = Date.now(); + if (window.AstrBotPluginPage) { + resolve(window.AstrBotPluginPage); + return; + } + var timer = setInterval(function () { + if (window.AstrBotPluginPage) { + clearInterval(timer); + resolve(window.AstrBotPluginPage); + return; + } + if (Date.now() - started > timeoutMs) { + clearInterval(timer); + resolve(null); + } + }, 80); + }); + } + + function deriveReachableUrl(webAdmin) { + webAdmin = webAdmin || {}; + var port = Number(webAdmin.port || 4100); + if (webAdmin.bind_all && window.location && window.location.hostname) { + return window.location.protocol + "//" + window.location.hostname + ":" + port + "/"; + } + return webAdmin.url || DEFAULT_URL; + } + + function renderFrame(url) { + var wrap = byId("frame-wrap"); + if (!wrap) return; + wrap.innerHTML = ""; + var frame = document.createElement("iframe"); + frame.title = "主动消息 Web 管理端"; + frame.src = url; + wrap.appendChild(frame); + } + + function render(payload) { + payload = payload || {}; + var status = payload.status || {}; + var webAdmin = payload.web_admin || {}; + var jobs = Array.isArray(payload.jobs) ? payload.jobs : []; + var sessions = Array.isArray(payload.sessions) ? payload.sessions : []; + var url = deriveReachableUrl(webAdmin); + state.webUrl = url; + + text("page-subtitle", "已连接 Pages,复用现有 Web 管理端"); + text("running-value", status.running ? "运行中" : "未运行"); + text("version-value", "版本 " + (status.version || "--")); + text("jobs-value", jobs.length || status.jobs_count || 0); + text("sessions-value", "会话 " + (sessions.length || status.sessions_count || 0)); + text("web-value", webAdmin.running ? "已启动" : (webAdmin.enabled ? "待启动" : "已禁用")); + text("web-hint", url); + + var openButton = byId("open-button"); + if (openButton) openButton.href = url; + renderFrame(url); + } + + function renderFallback(message) { + text("page-subtitle", message || "未连接 Pages bridge,使用默认 Web 管理端地址"); + text("running-value", "未知"); + text("version-value", "版本 --"); + text("jobs-value", "--"); + text("sessions-value", "会话 --"); + text("web-value", "默认入口"); + text("web-hint", DEFAULT_URL); + var openButton = byId("open-button"); + if (openButton) openButton.href = DEFAULT_URL; + renderFrame(DEFAULT_URL); + } + + function loadDashboard() { + if (!state.bridge || typeof state.bridge.apiGet !== "function") { + renderFallback("AstrBot Pages bridge 未注入,使用默认 Web 管理端地址"); + return Promise.resolve(); + } + return Promise.resolve(state.bridge.apiGet("dashboard")) + .then(function (payload) { + state.payload = normalizePayload(payload); + render(state.payload); + }) + .catch(function (error) { + renderFallback(error && error.message ? error.message : "读取 Pages 状态失败"); + }); + } + + function init() { + var openButton = byId("open-button"); + if (openButton) openButton.href = DEFAULT_URL; + var refreshButton = byId("refresh-button"); + if (refreshButton) refreshButton.addEventListener("click", loadDashboard); + + waitBridge(5000).then(function (bridge) { + state.bridge = bridge; + if (bridge && typeof bridge.ready === "function") { + Promise.resolve(bridge.ready()).finally(loadDashboard); + } else { + loadDashboard(); + } + }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); From 5d447b0c1e084bfa59109bd6cd1d5c929b77d482 Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 21:16:16 +0800 Subject: [PATCH 2/8] feat: replace pages bridge with native console --- core/task_scheduler.py | 32 + core/web_admin_server.py | 991 +++++- pages/proactive-chat/css/style.css | 3540 +++++++++++++++++++++ pages/proactive-chat/fonts/outfit-400.ttf | Bin 0 -> 47844 bytes pages/proactive-chat/fonts/outfit-500.ttf | Bin 0 -> 47800 bytes pages/proactive-chat/fonts/outfit-600.ttf | Bin 0 -> 48456 bytes pages/proactive-chat/fonts/outfit-700.ttf | Bin 0 -> 48340 bytes pages/proactive-chat/fonts/outfit-800.ttf | Bin 0 -> 48272 bytes pages/proactive-chat/fonts/outfit.css | 45 + pages/proactive-chat/index.html | 229 +- pages/proactive-chat/logo.png | Bin 0 -> 69222 bytes pages/proactive-chat/page-app.css | 900 ++++++ pages/proactive-chat/page-app.js | 1479 ++++++++- 13 files changed, 6848 insertions(+), 368 deletions(-) create mode 100644 pages/proactive-chat/css/style.css create mode 100644 pages/proactive-chat/fonts/outfit-400.ttf create mode 100644 pages/proactive-chat/fonts/outfit-500.ttf create mode 100644 pages/proactive-chat/fonts/outfit-600.ttf create mode 100644 pages/proactive-chat/fonts/outfit-700.ttf create mode 100644 pages/proactive-chat/fonts/outfit-800.ttf create mode 100644 pages/proactive-chat/fonts/outfit.css create mode 100644 pages/proactive-chat/logo.png create mode 100644 pages/proactive-chat/page-app.css diff --git a/core/task_scheduler.py b/core/task_scheduler.py index 92b1344..ee90643 100644 --- a/core/task_scheduler.py +++ b/core/task_scheduler.py @@ -131,6 +131,38 @@ def _is_persisted_task_still_valid( # 与 APScheduler misfire_grace_time 保持一致,允许 60 秒轻微抖动 return check_time < (next_trigger + 60) + def _get_max_unanswered_count( + self, session_config: dict | None, default: int = 3 + ) -> int: + if not isinstance(session_config, dict): + return default + schedule_conf = session_config.get("schedule_settings", {}) + if not isinstance(schedule_conf, dict): + return default + try: + return int(schedule_conf.get("max_unanswered_times", default) or 0) + except (TypeError, ValueError): + return default + + def _is_unanswered_limit_reached( + self, + session_id: str, + session_config: dict | None, + unanswered_count: int | None = None, + ) -> bool: + max_unanswered = self._get_max_unanswered_count(session_config) + if max_unanswered <= 0: + return False + if unanswered_count is None: + try: + unanswered_count = int( + self.session_data.get(session_id, {}).get("unanswered_count", 0) + or 0 + ) + except (TypeError, ValueError): + unanswered_count = 0 + return unanswered_count >= max_unanswered + def _clear_session_schedule_state( self, session_id: str, diff --git a/core/web_admin_server.py b/core/web_admin_server.py index c53665e..2e80bad 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio +import inspect import json import math import os @@ -13,6 +14,7 @@ from datetime import datetime from pathlib import Path from typing import Any +from urllib.parse import unquote from astrbot.api import logger @@ -34,6 +36,43 @@ "[主动消息] FastAPI 未安装喵,Web 管理端不可用喵。请安装: pip install fastapi uvicorn" ) +try: + from quart import request as quart_request +except ImportError: + quart_request = None + + +def _patch_starlette_router_startup_kwargs() -> None: + """兼容 FastAPI 与较新 Starlette Router 的启动参数签名差异。""" + try: + from starlette.routing import Router + except Exception as e: + logger.debug(f"[主动消息] 检查 Starlette Router 兼容性失败喵: {e}") + return + + init = Router.__init__ + if getattr(init, "_proactive_chat_startup_patch", False): + return + + try: + params = inspect.signature(init).parameters + except (TypeError, ValueError) as e: + logger.debug(f"[主动消息] 读取 Starlette Router 签名失败喵: {e}") + return + + unsupported = {name for name in ("on_startup", "on_shutdown") if name not in params} + if not unsupported: + return + + def patched_init(self, *args, **kwargs): + for name in unsupported: + kwargs.pop(name, None) + return init(self, *args, **kwargs) + + patched_init._proactive_chat_startup_patch = True + Router.__init__ = patched_init + logger.info("[主动消息] 已应用 FastAPI / Starlette Router 启动参数兼容补丁喵。") + def _is_running_in_docker() -> bool: """检测当前进程是否运行在 Docker / 容器环境中。""" @@ -57,14 +96,18 @@ def _is_running_in_docker() -> bool: class WebAdminServer: - ASTRBOT_PLUGIN_NAME = "astrbot_plugin_proactive_chat" """主动消息插件 Web 管理端服务器。""" + ASTRBOT_PLUGIN_NAME = "astrbot_plugin_proactive_chat" + ASTRBOT_PAGE_API_ENDPOINT = "dashboard" + ASTRBOT_PAGE_API_PATH = f"/{ASTRBOT_PLUGIN_NAME}/{ASTRBOT_PAGE_API_ENDPOINT}" + def __init__(self, plugin: Any): # plugin 是主插件实例,Web 端所有状态与操作都通过它间接访问。 self.plugin = plugin # 直接缓存配置引用,便于路由中统一读写。 self.config = plugin.config + self._native_config = self.config if hasattr(self.config, "save_config") else None # FastAPI 应用实例,仅在依赖存在且初始化成功时设置。 self.app: FastAPI | None = None # Uvicorn Server 实例,用于控制启动与停止。 @@ -102,76 +145,250 @@ def __init__(self, plugin: Any): f" 可能是 FastAPI / Pydantic 依赖版本不兼容: {e}" ) + def _decode_route_umo(self, umo: str) -> str: + """Decode URL-encoded UMO path parameters from Pages/Web API routes.""" + try: + return unquote(str(umo or "")) + except Exception: + return str(umo or "") + def register_astrbot_page_api(self) -> None: - """Register the lightweight AstrBot Pages API used by the plugin card.""" + """向 AstrBot WebUI 插件 Page 暴露完整管理端接口。""" context = getattr(self.plugin, "context", None) register = getattr(context, "register_web_api", None) if not callable(register): logger.debug( - "[主动消息] 当前 AstrBot 版本未提供 register_web_api,跳过 Pages 接口注册喵。" + "[主动消息] 当前 AstrBot 版本未提供 register_web_api,跳过插件卡片接口注册喵。" ) return - path = f"/{self.ASTRBOT_PLUGIN_NAME}/dashboard" - attempts = ( - lambda: register( - path, - self.build_astrbot_page_payload, - ["GET"], - "Proactive Chat page dashboard", + routes = ( + ("dashboard", self.build_astrbot_page_payload, ["GET"]), + ("embedded-dashboard", self.build_astrbot_page_payload, ["GET"]), + ("auth-info", self._page_auth_info, ["GET"]), + ("login", self._page_login, ["POST"]), + ("status", self._page_status, ["GET"]), + ("config", self._page_config, ["GET", "POST"]), + ("config-save", self._page_update_config, ["POST"]), + ("get_config", self._page_get_config, ["GET"]), + ("save_config", self._page_save_config, ["POST"]), + ("config-schema", self._page_get_config_schema, ["GET"]), + ("session-config/sessions", self._page_list_session_configs, ["GET"]), + ("session-config/", self._page_session_config, ["GET", "POST"]), + ( + "session-config-save/", + self._page_update_session_config, + ["POST"], + ), + ( + "session-config-delete/", + self._page_reset_session_config, + ["POST"], ), - lambda: register( - path, - self.build_astrbot_page_payload, - methods=["GET"], + ("jobs", self._page_list_jobs, ["GET"]), + ("jobs//reschedule", self._page_reschedule_job, ["POST"]), + ("jobs//trigger", self._page_trigger_job, ["POST"]), + ("jobs-cancel/", self._page_cancel_job, ["POST"]), + ("notifications", self._page_get_notifications, ["GET"]), + ("notifications/read", self._page_mark_notification_read, ["POST"]), + ( + "notifications/read-all", + self._page_mark_all_notifications_read, + ["POST"], ), - lambda: register("GET", path, self.build_astrbot_page_payload), + ("notifications/refresh", self._page_refresh_notifications, ["POST"]), + ("markdown-files", self._page_list_markdown_files, ["GET"]), + ("markdown-files/", self._page_get_markdown_file, ["GET"]), + ("open-directory", self._page_open_directory, ["POST"]), ) + + registered_count = 0 + for endpoint, handler, methods in routes: + path = f"/{self.ASTRBOT_PLUGIN_NAME}/{endpoint}" + if self._register_astrbot_page_route(register, path, handler, methods): + registered_count += 1 + + logger.info( + f"[主动消息] 已注册 AstrBot 插件卡片管理端接口 {registered_count}/{len(routes)} 个喵。" + ) + + def _register_astrbot_page_route( + self, + register, + path: str, + handler, + methods: list[str], + ) -> bool: + """兼容不同 AstrBot 版本的 register_web_api 签名。""" + attempts = ( + lambda: register(path, handler, methods, "Proactive chat WebUI API"), + lambda: register(path, handler, methods=methods), + lambda: register(methods[0], path, handler), + lambda: register(path, handler), + ) + + last_error: Exception | None = None for attempt in attempts: try: attempt() - logger.info("[主动消息] 已注册 AstrBot Pages 管理入口接口喵。") - return - except TypeError: + return True + except TypeError as e: + last_error = e continue except Exception as e: - logger.debug(f"[主动消息] 注册 AstrBot Pages 接口失败喵: {e}") - return + logger.warning( + f"[主动消息] 注册 AstrBot 插件卡片接口失败喵: {path}: {e}" + ) + return False - logger.debug( - "[主动消息] 当前 AstrBot register_web_api 签名不兼容,跳过 Pages 接口注册喵。" - ) + if last_error: + logger.debug( + f"[主动消息] AstrBot 插件卡片接口签名不兼容,已跳过 {path} 喵: {last_error}" + ) + return False - def _build_web_admin_url_payload(self) -> dict[str, Any]: - web_admin = self.config.get("web_admin", {}) - host = str(web_admin.get("host", "127.0.0.1") or "127.0.0.1") - try: - port = int(web_admin.get("port", 4100)) - except (TypeError, ValueError): - port = 4100 + async def _read_astrbot_page_json(self) -> dict[str, Any]: + if quart_request is None: + return {} - bind_all = host in {"0.0.0.0", "::", ""} - display_host = "127.0.0.1" if bind_all else host - return { - "enabled": bool(web_admin.get("enabled", True)), - "available": bool(self._web_admin_available), - "running": bool(self.server_task and not self.server_task.done()), - "host": host, - "port": port, - "bind_all": bind_all, - "url": f"http://{display_host}:{port}/", - } + payload = None + for kwargs in ({"silent": True}, {"force": True}, {}): + try: + payload = await quart_request.get_json(**kwargs) + if payload is not None: + break + except TypeError: + continue + except Exception: + continue + return self._unwrap_page_payload(payload) - async def build_astrbot_page_payload(self) -> dict[str, Any]: - """Build a small runtime snapshot for the AstrBot Pages entry.""" + def _unwrap_page_payload(self, payload: Any) -> dict[str, Any]: + if isinstance(payload, str): + try: + payload = json.loads(payload) + except Exception: + return {} + if not isinstance(payload, dict): + return {} + + for key in ("body", "data", "payload"): + if set(payload.keys()) != {key}: + continue + nested = payload.get(key) + if isinstance(nested, str): + try: + nested = json.loads(nested) + except Exception: + return {} + if isinstance(nested, dict): + return nested + return payload + + def _get_astrbot_page_method(self) -> str: + if quart_request is None: + return "GET" + return str(getattr(quart_request, "method", "GET") or "GET").upper() + + async def _page_auth_info(self): + return {"auth_required": False} + + async def _page_login(self): + return {"token": "page-bridge", "auth_required": False} + + async def _page_status(self): + return self._build_status_payload() + + async def _page_get_config(self): + return self._build_config_payload() + + async def _page_update_config(self): + payload = await self._read_astrbot_page_json() + return await self._apply_config_payload(payload) + + async def _page_save_config(self): + payload = await self._read_astrbot_page_json() + logger.info( + f"[主动消息] Pages save_config 收到保存请求喵,字段: {sorted(payload.keys())}" + ) + result = await self._apply_config_payload(payload) + if not result.get("ok", False): + return { + "success": False, + "error": result.get("error") or result.get("message") or "保存失败", + "received": True, + "received_keys": sorted(payload.keys()), + } return { - "status": self._build_status_payload(), - "jobs": self._collect_jobs(), - "sessions": self._list_known_session_summaries(), - "web_admin": self._build_web_admin_url_payload(), + "success": True, + "received": True, + "received_keys": sorted(payload.keys()), + "config": result.get("config") or self._build_config_payload(), } + async def _page_config(self): + if self._get_astrbot_page_method() == "POST": + return await self._page_update_config() + return await self._page_get_config() + + async def _page_get_config_schema(self): + return await self._load_config_schema_payload() + + async def _page_list_session_configs(self): + return {"sessions": self._list_session_config_payloads()} + + async def _page_get_session_config(self, umo: str): + return self._build_session_config_payload(umo) + + async def _page_update_session_config(self, umo: str): + payload = await self._read_astrbot_page_json() + return await self._apply_session_config_payload(umo, payload) + + async def _page_session_config(self, umo: str): + if self._get_astrbot_page_method() == "POST": + return await self._page_update_session_config(umo) + return await self._page_get_session_config(umo) + + async def _page_reset_session_config(self, umo: str): + return await self._reset_session_config_payload(umo) + + async def _page_list_jobs(self): + return {"jobs": self._collect_jobs()} + + async def _page_reschedule_job(self, umo: str): + return await self._reschedule_job_payload(umo) + + async def _page_trigger_job(self, umo: str): + return await self._trigger_job_payload(umo) + + async def _page_cancel_job(self, umo: str): + return await self._cancel_job_payload(umo) + + async def _page_get_notifications(self): + return await self._build_notification_payload() + + async def _page_mark_notification_read(self): + payload = await self._read_astrbot_page_json() + return await self._mark_notification_read_payload(payload) + + async def _page_mark_all_notifications_read(self): + return await self._mark_all_notifications_read_payload() + + async def _page_refresh_notifications(self): + return await self._refresh_notifications_payload() + + async def _page_list_markdown_files(self): + return {"items": self._list_markdown_documents()} + + async def _page_get_markdown_file(self, file_path: str): + return await self._build_markdown_file_payload(file_path) + + async def _page_open_directory(self): + payload = await self._read_astrbot_page_json() + return await self._open_directory_payload(payload) + def _setup_app(self) -> None: + _patch_starlette_router_startup_kwargs() # 创建 FastAPI 应用,版本号用于控制台元信息展示。 self.app = FastAPI( title="主动消息管理端", @@ -275,6 +492,11 @@ async def get_status(): # 汇总插件运行状态、计时器与连接数,供首页卡片与轮询逻辑使用。 return self._build_status_payload() + @self.app.get("/api/embedded-dashboard") + async def embedded_dashboard(): + # 给 AstrBot 插件 Page 或其它轻量入口复用的聚合快照。 + return await self.build_astrbot_page_payload() + @self.app.get("/api/markdown-files") async def list_markdown_files(): # 仅暴露插件目录内明确允许浏览的 Markdown 文档,避免前端任意探测文件系统。 @@ -349,27 +571,10 @@ async def get_config_schema(): @self.app.post("/api/config") async def update_config(payload: dict[str, Any]): - # 仅允许更新这三个一级配置块,避免前端误写其它未知字段。 - allowed_keys = {"friend_settings", "group_settings", "web_admin"} - for key in allowed_keys: - if key not in payload: - continue - if key == "web_admin": - # web_admin 采用增量合并,避免未提交字段被整个覆盖掉。 - old = dict(self.config.get("web_admin", {})) - old.update(payload.get("web_admin", {})) - # 密码字段允许显式更新,但不会通过 get_config 回传给前端。 - if "password" in payload.get("web_admin", {}): - old["password"] = payload["web_admin"]["password"] - self.config["web_admin"] = old - else: - # friend / group 配置块按前端提交的完整对象直接替换。 - self.config[key] = payload[key] - - self._save_plugin_config() - # 配置变更后立即广播,确保所有已打开页面实时刷新。 - await self._broadcast_update("config") - return {"ok": True} + result = await self._apply_config_payload(payload) + if not result.get("ok", False): + return JSONResponse(result, status_code=400) + return result @self.app.get("/api/session-config/sessions") async def list_session_configs(): @@ -407,7 +612,7 @@ async def list_session_configs(): @self.app.get("/api/session-config/{umo:path}") async def get_session_config(umo: str): # 路径参数使用 path 转换器,允许会话 ID 中包含斜杠等特殊字符。 - normalized = self.plugin._normalize_session_id(umo) + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) base = self.plugin._get_base_session_config(normalized) return { "session": normalized, @@ -423,7 +628,7 @@ async def get_session_config(umo: str): @self.app.post("/api/session-config/{umo:path}") async def update_session_config(umo: str, payload: dict[str, Any]): - normalized = self.plugin._normalize_session_id(umo) + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) # mode 用于兼容两种写法:直接提交 override,或提交最终 effective 配置。 mode = payload.get("mode", "effective") @@ -473,7 +678,7 @@ async def update_session_config(umo: str, payload: dict[str, Any]): @self.app.delete("/api/session-config/{umo:path}") async def reset_session_config(umo: str): # 删除覆写后,会话会重新完全继承全局配置。 - normalized = self.plugin._normalize_session_id(umo) + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) await self.plugin.session_override_manager.delete_override(normalized) await self._broadcast_update("session-config") return { @@ -490,7 +695,7 @@ async def list_jobs(): @self.app.post("/api/jobs/{umo:path}/reschedule") async def reschedule_job(umo: str): - normalized = self.plugin._normalize_session_id(umo) + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) session_config = self.plugin._get_session_config(normalized) if not session_config or not session_config.get("enable", False): return JSONResponse( @@ -670,7 +875,7 @@ async def open_directory(payload: dict[str, Any]): @self.app.post("/api/jobs/{umo:path}/trigger") async def trigger_job(umo: str): # 立即手动触发一次指定会话的检查与发言流程;同一会话在执行完成前禁止重复触发。 - normalized = self.plugin._normalize_session_id(umo) + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) if normalized in self.plugin.manual_trigger_sessions: return JSONResponse( { @@ -695,7 +900,7 @@ async def trigger_job(umo: str): @self.app.delete("/api/jobs/{umo:path}") async def cancel_job(umo: str): - normalized = self.plugin._normalize_session_id(umo) + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) removed = False try: # APScheduler 中的 job id 直接使用规范化后的 session id。 @@ -798,13 +1003,31 @@ async def websocket_endpoint(websocket: WebSocket): if websocket in self._ws_connections: self._ws_connections.remove(websocket) - def _save_plugin_config(self) -> None: + def _set_config_section(self, key: str, value: Any) -> None: + self.config[key] = value + + def _save_plugin_config(self) -> tuple[bool, str | None]: try: # AstrBot 配置对象通常提供 save_config 方法,这里做鸭子类型兼容。 - if hasattr(self.config, "save_config"): - self.config.save_config() + native = self._native_config or ( + self.config if hasattr(self.config, "save_config") else None + ) + if native is not None: + if native is not self.config: + native.clear() + native.update(self.config) + native.save_config() + + context = getattr(self.plugin, "context", None) + update_config = getattr(context, "update_config", None) + if callable(update_config): + updated = update_config(self.config) + if inspect.isawaitable(updated): + asyncio.create_task(updated) + return True, None except Exception as e: logger.warning(f"[主动消息] 保存配置失败喵: {e}") + return False, str(e) def _issue_token(self) -> str: # 生成适合放入 URL / Header 的安全随机 token。 @@ -828,6 +1051,336 @@ def _verify_token(self, token: str) -> bool: return False return True + def _build_config_payload(self) -> dict[str, Any]: + web_admin = { + k: v for k, v in self.config.get("web_admin", {}).items() if k != "password" + } + return { + "friend_settings": dict(self.config.get("friend_settings", {})), + "group_settings": dict(self.config.get("group_settings", {})), + "web_admin": web_admin, + "notification_settings": dict(self.config.get("notification_settings", {})), + } + + async def _apply_config_payload(self, payload: dict[str, Any]) -> dict[str, Any]: + payload = self._unwrap_page_payload(payload) + allowed_keys = { + "friend_settings", + "group_settings", + "web_admin", + "notification_settings", + } + changed = False + for key in allowed_keys: + if key not in payload: + continue + if key != "web_admin" and not isinstance(payload[key], dict): + return {"ok": False, "error": f"{key} must be an object"} + if key == "web_admin": + if not isinstance(payload.get("web_admin"), dict): + return {"ok": False, "error": "web_admin must be an object"} + old = dict(self.config.get("web_admin", {})) + old.update(payload.get("web_admin", {})) + if "password" in payload.get("web_admin", {}): + old["password"] = payload["web_admin"]["password"] + self._set_config_section("web_admin", old) + else: + self._set_config_section(key, payload[key]) + changed = True + + if not changed: + return {"ok": False, "error": "No supported config keys received"} + + saved, error = self._save_plugin_config() + if not saved: + return {"ok": False, "error": error or "Config save failed"} + self._auth_enabled = bool(self.config.get("web_admin", {}).get("password", "")) + await self._broadcast_update("config") + return {"ok": True, "config": self._build_config_payload()} + + async def _load_config_schema_payload(self) -> dict[str, Any]: + schema_path = Path(__file__).resolve().parent.parent / "_conf_schema.json" + if schema_path.exists(): + try: + schema_text = await asyncio.to_thread( + schema_path.read_text, encoding="utf-8" + ) + return json.loads(schema_text) + except Exception as e: + logger.error(f"[主动消息] 读取 Schema 失败喵: {e}") + return {} + + def _list_session_config_payloads(self) -> list[dict[str, Any]]: + result = [] + for session in self._list_known_sessions(): + override = self.plugin.session_override_manager.get_override(session) + effective = self.plugin._get_session_config(session) + session_name = self.plugin._get_session_name(session, effective) + result.append( + { + "session": session, + "session_name": session_name, + "session_display_name": self.plugin._get_session_display_name( + session, effective + ), + "has_override": bool(override), + "override_keys": list(override.keys()), + "enabled": bool(effective and effective.get("enable", False)), + "next_trigger_time": self.plugin.session_data.get(session, {}).get( + "next_trigger_time" + ), + "unanswered_count": self.plugin.session_data.get(session, {}).get( + "unanswered_count", 0 + ), + } + ) + return result + + def _build_session_config_payload(self, umo: str) -> dict[str, Any]: + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) + base = self.plugin._get_base_session_config(normalized) + return { + "session": normalized, + "base": base, + "override": self.plugin.session_override_manager.get_override(normalized), + "effective": self.plugin._get_session_config(normalized), + } + + async def _apply_session_config_payload( + self, umo: str, payload: dict[str, Any] + ) -> dict[str, Any]: + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) + mode = payload.get("mode", "effective") + + if mode == "override": + override = payload.get("override", {}) + if not isinstance(override, dict): + return {"ok": False, "error": "override 必须是对象"} + await self.plugin.session_override_manager.set_override( + normalized, override + ) + else: + effective = payload.get("effective", {}) + if not isinstance(effective, dict): + return {"ok": False, "error": "effective 必须是对象"} + base = self.plugin._get_base_session_config(normalized) + if not base: + return { + "ok": False, + "error": "会话未命中 friend/group 全局配置,无法保存 effective", + } + await self.plugin.session_override_manager.update_session_from_effective( + normalized, + base, + effective, + ) + + await self._broadcast_update("session-config") + return { + "ok": True, + "session": normalized, + "override": self.plugin.session_override_manager.get_override(normalized), + "effective": self.plugin._get_session_config(normalized), + } + + async def _reset_session_config_payload(self, umo: str) -> dict[str, Any]: + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) + await self.plugin.session_override_manager.delete_override(normalized) + await self._broadcast_update("session-config") + return { + "ok": True, + "session": normalized, + "override": {}, + "effective": self.plugin._get_session_config(normalized), + } + + async def _reschedule_job_payload(self, umo: str) -> dict[str, Any]: + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) + session_config = self.plugin._get_session_config(normalized) + if not session_config or not session_config.get("enable", False): + return { + "ok": False, + "session": normalized, + "error": "会话未启用或配置不存在,无法重新调度", + } + + await self.plugin._schedule_next_chat_and_save(normalized, reset_counter=False) + await self._broadcast_update("jobs") + return { + "ok": True, + "session": normalized, + "message": "已重新调度下一次主动消息时间", + } + + async def _trigger_job_payload(self, umo: str) -> dict[str, Any]: + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) + if normalized in self.plugin.manual_trigger_sessions: + return { + "ok": False, + "session": normalized, + "in_progress": True, + "message": "该任务正在立即触发中,请等待当前执行完成", + } + + self.plugin.manual_trigger_sessions.add(normalized) + asyncio.create_task(self.plugin.check_and_chat(normalized)) + await self._broadcast_update("jobs") + return { + "ok": True, + "session": normalized, + "in_progress": True, + "message": "已开始立即触发,正在等待 LLM 完成回复", + } + + async def _cancel_job_payload(self, umo: str) -> dict[str, Any]: + normalized = self.plugin._normalize_session_id(self._decode_route_umo(umo)) + removed = False + try: + self.plugin.scheduler.remove_job(normalized) + removed = True + except Exception: + pass + + async with self.plugin.data_lock: + if normalized in self.plugin.session_data: + self.plugin.session_data[normalized].pop("next_trigger_time", None) + await self.plugin._save_data_internal() + + await self._broadcast_update("jobs") + return {"ok": True, "session": normalized, "removed": removed} + + async def _mark_notification_read_payload( + self, payload: dict[str, Any] + ) -> dict[str, Any]: + if not getattr(self.plugin, "notification_center", None): + return {"ok": False, "error": "通知系统不可用"} + + notification_id = payload.get("id") + if notification_id is None: + return {"ok": False, "error": "缺少必填字段 id"} + try: + normalized_id = int(notification_id) + except (TypeError, ValueError): + return {"ok": False, "error": "id 必须是数字"} + + result = await self.plugin.notification_center.mark_as_read(normalized_id) + await self._broadcast_update("notifications") + return result + + async def _mark_all_notifications_read_payload(self) -> dict[str, Any]: + if not getattr(self.plugin, "notification_center", None): + return {"ok": False, "error": "通知系统不可用"} + result = await self.plugin.notification_center.mark_all_as_read() + await self._broadcast_update("notifications") + return result + + async def _refresh_notifications_payload(self) -> dict[str, Any]: + if not getattr(self.plugin, "notification_center", None): + return {"ok": False, "error": "通知系统不可用"} + changed = await self.plugin.notification_center.refresh() + await self._broadcast_update("notifications") + payload = await self.plugin.notification_center.get_payload() + return { + "ok": True, + "changed": changed, + "items": payload.get("items", []), + "meta": payload.get("meta", {}), + } + + async def _build_markdown_file_payload(self, file_path: str) -> dict[str, Any]: + resolved = self._resolve_markdown_document(file_path) + if not resolved: + return {"ok": False, "error": "文档不存在或不允许访问"} + + try: + content = await asyncio.to_thread(resolved.read_text, encoding="utf-8") + except UnicodeDecodeError: + return { + "ok": False, + "error": "文档编码不受支持,仅支持 UTF-8 Markdown 文件", + } + except Exception as e: + logger.error(f"[主动消息] 读取 Markdown 文档失败喵: {e}") + return {"ok": False, "error": "读取文档失败", "message": str(e)} + + return { + "path": self._to_workspace_relative_path(resolved), + "title": resolved.stem, + "content": content, + "content_format": "markdown", + } + + async def _open_directory_payload(self, payload: dict[str, Any]) -> dict[str, Any]: + target = str(payload.get("path", "plugin")).strip().lower() + directory = ( + Path(self.plugin.data_dir) + if target == "data" + else Path(__file__).resolve().parent.parent + ) + + try: + directory.mkdir(parents=True, exist_ok=True) + dir_str = str(directory) + + if _is_running_in_docker(): + return { + "ok": False, + "error": "Docker 环境下不支持在宿主机直接打开目录,请手动查看挂载路径", + "path": dir_str, + } + + if os.name == "nt": + await asyncio.to_thread(os.startfile, dir_str) + elif sys.platform == "darwin": + result = await asyncio.to_thread( + subprocess.run, + ["open", dir_str], + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + detail = (result.stderr or result.stdout or "未知错误").strip() + return { + "ok": False, + "error": "打开目录失败(macOS)", + "message": f"open 命令执行失败: {detail}", + "path": dir_str, + } + else: + result = await asyncio.to_thread( + subprocess.run, + ["xdg-open", dir_str], + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + detail = (result.stderr or result.stdout or "未知错误").strip() + return { + "ok": False, + "error": "打开目录失败(Linux)", + "message": ( + "xdg-open 执行失败,服务器可能缺少桌面环境或未安装 xdg-open: " + f"{detail}" + ), + "path": dir_str, + } + + return { + "ok": True, + "path": dir_str, + "message": "已在系统文件管理器中打开目录", + } + except Exception as e: + logger.error(f"[主动消息] 打开目录失败喵: {e}") + return { + "ok": False, + "error": "打开目录失败", + "message": str(e), + "path": str(directory), + } + def _safe_timer_meta(self, timer: Any, now: float) -> dict[str, float | int | None]: # 某些会话可能当前没有有效 timer,此时直接返回空元信息。 if timer is None: @@ -1011,6 +1564,115 @@ def _collect_timer_cards(self, now: float) -> dict[str, list[dict[str, Any]]]: ) # 统一按剩余时间升序排序,让最接近触发的卡片优先显示。 + live_auto_sessions = { + str(card.get("session_id", "")) for card in auto_cards if card.get("session_id") + } + live_group_sessions = { + str(card.get("session_id", "")) + for card in group_cards + if card.get("session_id") + } + + # Expose configured sessions even when no asyncio timer handle is currently + # registered. Without these placeholders the Pages dashboard looks empty, + # while the real state is often "waiting for the first message/event". + for session_id in self._list_known_sessions(): + normalized_session_id = self.plugin._normalize_session_id(str(session_id)) + session_config = self.plugin._get_session_config(normalized_session_id) + if not session_config or not session_config.get("enable", False): + continue + + session_category = self._detect_session_category(normalized_session_id) + session_data = self.plugin.session_data.get( + normalized_session_id, self.plugin.session_data.get(str(session_id), {}) + ) + schedule_settings = session_config.get("schedule_settings", {}) + context_settings = session_config.get("context_settings", {}) + common_payload = { + "session_id": normalized_session_id, + "session_name": self.plugin._get_session_name( + normalized_session_id, session_config + ), + "session_display_name": self.plugin._get_session_display_name( + normalized_session_id, session_config + ), + "session_category": session_category, + "source_mode": context_settings.get( + "source_mode", + "platform_message_history" + if session_category == "group" + else "conversation_history", + ), + "max_unanswered_times": schedule_settings.get( + "max_unanswered_times", 0 + ), + "remaining_seconds": None, + "target_time": None, + "progress_percent": 0, + "unanswered_count": session_data.get("unanswered_count", 0), + } + + if session_category == "group": + if normalized_session_id in live_group_sessions: + continue + + idle_minutes = int( + session_config.get("group_idle_trigger_minutes", 0) or 0 + ) + if idle_minutes <= 0: + continue + + group_cards.append( + { + **common_payload, + "timer_kind": "group_silence", + "title": "群沉默检测", + "timer_kind_label": "群沉默检测", + "status": "waiting_message", + "window_seconds": idle_minutes * 60, + "group_idle_trigger_minutes": idle_minutes, + "last_message_time": self.plugin.last_message_times.get( + normalized_session_id + ) + or None, + "inactive_reason": "等待群聊新消息后开始沉默倒计时", + "is_live_group_timer": False, + } + ) + live_group_sessions.add(normalized_session_id) + continue + + if normalized_session_id in live_auto_sessions: + continue + + auto_settings = session_config.get("auto_trigger_settings", {}) + if not auto_settings.get("enable_auto_trigger", False): + continue + + trigger_delay_minutes = int( + auto_settings.get("auto_trigger_after_minutes", 0) or 0 + ) + if trigger_delay_minutes <= 0: + continue + + last_message_time = self.plugin.last_message_times.get( + normalized_session_id + ) or self.plugin.last_message_times.get(str(session_id), 0) + auto_cards.append( + { + **common_payload, + "timer_kind": "auto_trigger", + "title": "自动触发检测", + "timer_kind_label": "自动触发检测", + "status": "waiting_idle" if last_message_time else "pending_timer", + "window_seconds": trigger_delay_minutes * 60, + "auto_trigger_after_minutes": trigger_delay_minutes, + "last_message_time": last_message_time or None, + "inactive_reason": "当前没有运行中的自动触发计时器,等待运行时注册或下一次消息事件", + } + ) + live_auto_sessions.add(normalized_session_id) + auto_cards.sort( key=lambda item: ( item.get("remaining_seconds") is None, @@ -1067,21 +1729,57 @@ def _build_status_payload(self) -> dict[str, Any]: "timestamp": datetime.now().isoformat(), } - def _collect_jobs(self) -> list[dict[str, Any]]: - if not self.plugin.scheduler: - return [] + def _build_web_admin_url(self) -> str | None: + web_admin = self.config.get("web_admin", {}) + if not web_admin.get("enabled", False): + return None + + host = str(web_admin.get("host", "127.0.0.1") or "127.0.0.1") + port = int(web_admin.get("port", 4100) or 4100) + display_host = "127.0.0.1" if host in {"0.0.0.0", "::"} else host + return f"http://{display_host}:{port}/" + async def build_astrbot_page_payload(self) -> dict[str, Any]: + """构造 AstrBot 插件卡片 Page 的轻量运行快照。""" + notifications = await self._build_notification_payload() + return { + "ok": True, + "status": self._build_status_payload(), + "jobs": self._collect_jobs(), + "sessions": self._list_known_session_summaries(), + "notifications_meta": notifications.get("meta", {}), + "web_admin": { + "available": bool(self._web_admin_available and self.app), + "enabled": bool(self.config.get("web_admin", {}).get("enabled", False)), + "url": self._build_web_admin_url(), + "auth_required": self._auth_enabled, + }, + "timestamp": datetime.now().isoformat(), + } + + def _collect_jobs(self) -> list[dict[str, Any]]: jobs = [] - for job in self.plugin.scheduler.get_jobs(): + seen_sessions: set[str] = set() + + for job in self.plugin.scheduler.get_jobs() if self.plugin.scheduler else []: session_id = str(job.id) + normalized_session_id = self.plugin._normalize_session_id(session_id) + seen_sessions.add(normalized_session_id) session_data = self.plugin.session_data.get(session_id, {}) session_config = self.plugin._get_session_config(session_id) or {} schedule_settings = session_config.get("schedule_settings", {}) context_settings = session_config.get("context_settings", {}) auto_trigger_settings = session_config.get("auto_trigger_settings", {}) + unanswered_count = session_data.get("unanswered_count", 0) + if self.plugin._is_unanswered_limit_reached( + normalized_session_id, session_config, unanswered_count + ): + continue jobs.append( { "id": session_id, + "status": "scheduled", + "status_label": "已调度", "session_name": self.plugin._get_session_name( session_id, session_config ), @@ -1100,7 +1798,7 @@ def _collect_jobs(self) -> list[dict[str, Any]]: "next_run_time": ( job.next_run_time.isoformat() if job.next_run_time else None ), - "unanswered_count": session_data.get("unanswered_count", 0), + "unanswered_count": unanswered_count, "manual_trigger_in_progress": session_id in self.plugin.manual_trigger_sessions, # 以下字段用于前端推导进度条与调度窗口说明。 @@ -1115,6 +1813,12 @@ def _collect_jobs(self) -> list[dict[str, Any]]: "last_schedule_random_interval_seconds": session_data.get( "last_schedule_random_interval_seconds" ), + "last_schedule_strategy": session_data.get( + "last_schedule_strategy" + ), + "last_schedule_reason": session_data.get("last_schedule_reason"), + "last_schedule_rule": session_data.get("last_schedule_rule"), + "last_schedule_source": session_data.get("last_schedule_source"), # 透出当前会话配置中的调度区间与免打扰时段,供任务卡片展示。 "schedule_min_interval_minutes": schedule_settings.get( "min_interval_minutes" @@ -1125,6 +1829,98 @@ def _collect_jobs(self) -> list[dict[str, Any]]: "quiet_hours": schedule_settings.get("quiet_hours", ""), } ) + + for session_id in self._list_known_sessions(): + normalized_session_id = self.plugin._normalize_session_id(str(session_id)) + if normalized_session_id in seen_sessions: + continue + + session_config = self.plugin._get_session_config(normalized_session_id) + if not session_config or not session_config.get("enable", False): + continue + + session_data = self.plugin.session_data.get( + normalized_session_id, self.plugin.session_data.get(str(session_id), {}) + ) + schedule_settings = session_config.get("schedule_settings", {}) + context_settings = session_config.get("context_settings", {}) + auto_trigger_settings = session_config.get("auto_trigger_settings", {}) + unanswered_count = session_data.get("unanswered_count", 0) + if self.plugin._is_unanswered_limit_reached( + normalized_session_id, session_config, unanswered_count + ): + continue + next_trigger_time = session_data.get("next_trigger_time") + next_run_time = None + if next_trigger_time: + try: + next_run_time = datetime.fromtimestamp( + float(next_trigger_time), + tz=getattr(self.plugin, "timezone", None), + ).isoformat() + except Exception: + next_run_time = None + + jobs.append( + { + "id": normalized_session_id, + "status": "pending_schedule", + "status_label": "待调度", + "session_name": self.plugin._get_session_name( + normalized_session_id, session_config + ), + "session_display_name": self.plugin._get_session_display_name( + normalized_session_id, session_config + ), + "session_category": self._detect_session_category( + normalized_session_id + ), + "source_mode": context_settings.get( + "source_mode", "conversation_history" + ), + "max_unanswered_times": schedule_settings.get( + "max_unanswered_times", + auto_trigger_settings.get("max_unanswered_times", 0), + ), + "next_run_time": next_run_time, + "unanswered_count": unanswered_count, + "manual_trigger_in_progress": normalized_session_id + in self.plugin.manual_trigger_sessions, + "next_trigger_time": next_trigger_time, + "last_scheduled_at": session_data.get("last_scheduled_at"), + "last_schedule_min_interval_seconds": session_data.get( + "last_schedule_min_interval_seconds" + ), + "last_schedule_max_interval_seconds": session_data.get( + "last_schedule_max_interval_seconds" + ), + "last_schedule_random_interval_seconds": session_data.get( + "last_schedule_random_interval_seconds" + ), + "last_schedule_strategy": session_data.get( + "last_schedule_strategy" + ), + "last_schedule_reason": session_data.get("last_schedule_reason"), + "last_schedule_rule": session_data.get("last_schedule_rule"), + "last_schedule_source": session_data.get("last_schedule_source"), + "schedule_min_interval_minutes": schedule_settings.get( + "min_interval_minutes" + ), + "schedule_max_interval_minutes": schedule_settings.get( + "max_interval_minutes" + ), + "quiet_hours": schedule_settings.get("quiet_hours", ""), + "inactive_reason": "当前没有 APScheduler 任务,可能正在等待下一次触发条件或需要重新调度", + } + ) + + jobs.sort( + key=lambda item: ( + item.get("next_run_time") is None, + item.get("next_run_time") or "", + item.get("id") or "", + ) + ) return jobs def _list_known_sessions(self) -> list[str]: @@ -1147,6 +1943,9 @@ def _list_known_session_summaries(self) -> list[dict[str, Any]]: result: list[dict[str, Any]] = [] for session in self._list_known_sessions(): effective = self.plugin._get_session_config(session) + session_data = self.plugin.session_data.get(session, {}) + schedule_settings = (effective or {}).get("schedule_settings", {}) + auto_trigger_settings = (effective or {}).get("auto_trigger_settings", {}) result.append( { "session": session, @@ -1163,6 +1962,34 @@ def _list_known_session_summaries(self) -> list[dict[str, Any]]: ), "manual_trigger_in_progress": session in self.plugin.manual_trigger_sessions, + "enabled": bool(effective and effective.get("enable", False)), + "session_category": self._detect_session_category(session), + "next_trigger_time": session_data.get("next_trigger_time"), + "last_scheduled_at": session_data.get("last_scheduled_at"), + "last_schedule_min_interval_seconds": session_data.get( + "last_schedule_min_interval_seconds" + ), + "last_schedule_max_interval_seconds": session_data.get( + "last_schedule_max_interval_seconds" + ), + "last_schedule_random_interval_seconds": session_data.get( + "last_schedule_random_interval_seconds" + ), + "last_schedule_strategy": session_data.get( + "last_schedule_strategy" + ), + "last_schedule_reason": session_data.get("last_schedule_reason"), + "last_schedule_rule": session_data.get("last_schedule_rule"), + "last_schedule_source": session_data.get("last_schedule_source"), + "schedule_min_interval_minutes": schedule_settings.get( + "min_interval_minutes" + ), + "schedule_max_interval_minutes": schedule_settings.get( + "max_interval_minutes" + ), + "auto_trigger_after_minutes": auto_trigger_settings.get( + "auto_trigger_after_minutes" + ), } ) return result diff --git a/pages/proactive-chat/css/style.css b/pages/proactive-chat/css/style.css new file mode 100644 index 0000000..2b6fb64 --- /dev/null +++ b/pages/proactive-chat/css/style.css @@ -0,0 +1,3540 @@ +/* Material Design 3 - 主动消息主题 */ +:root { + /* 整个管理端的设计令牌入口:亮色模式下所有组件都从这里读取颜色变量。 */ + /* --- MD3 颜色令牌 (紫色种子) --- */ + --md-sys-color-primary: #6750A4; + --md-sys-color-on-primary: #FFFFFF; + --md-sys-color-primary-container: #EADDFF; + --md-sys-color-on-primary-container: #21005D; + + --md-sys-color-secondary: #625B71; + --md-sys-color-on-secondary: #FFFFFF; + --md-sys-color-secondary-container: #E8DEF8; + --md-sys-color-on-secondary-container: #1D192B; + + --md-sys-color-surface: rgba(255, 255, 255, 0.8); + --md-sys-color-on-surface: #1C1B1F; + --md-sys-color-on-surface-variant: #49454F; + --md-sys-color-surface-variant: #E7E0EC; + + --md-sys-color-background: #FEF7FF; + --md-sys-color-outline-variant: rgba(103, 80, 164, 0.1); + + --glass-bg: rgba(255, 255, 255, 0.65); + --glass-border: rgba(103, 80, 164, 0.15); + --glass-shadow: 0 4px 20px 0 rgba(103, 80, 164, 0.08); + + /* Markdown 代码高亮颜色令牌,供亮暗主题统一覆写。 */ + --md-code-token-key: #7C3AED; + --md-code-token-string: #047857; + --md-code-token-number: #B45309; + --md-code-token-boolean: #C026D3; + --md-code-token-keyword: #2563EB; + --md-code-token-function: #7C2D12; + --md-code-token-command: #4338CA; + --md-code-token-flag: #0F766E; + --md-code-token-punctuation: rgba(28, 27, 31, 0.52); + + --theme-transition-duration: 220ms; + --interactive-transition-duration: 180ms; + --theme-transition-easing: cubic-bezier(0.4, 0, 0.2, 1); + + /* --- 形状 --- */ + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 20px; + --radius-xl: 28px; + + /* --- 顶栏 --- */ + --top-bar-height: 80px; +} + +/* 暗色主题通过覆盖 CSS 变量切换外观,尽量避免为每个组件单独写一套颜色。 */ +body.dark-theme { + --md-sys-color-primary: #D0BCFF; + --md-sys-color-on-primary: #381E72; + --md-sys-color-primary-container: #4F378B; + --md-sys-color-on-primary-container: #EADDFF; + + --md-sys-color-surface: rgba(28, 27, 31, 0.8); + --md-sys-color-on-surface: #E6E1E5; + --md-sys-color-on-surface-variant: #CAC4D0; + --md-sys-color-surface-variant: #49454F; + + --md-sys-color-background: #141218; + --md-sys-color-outline-variant: rgba(208, 188, 255, 0.1); + + --glass-bg: rgba(28, 27, 31, 0.7); + --glass-border: rgba(208, 188, 255, 0.1); + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.4); + + --md-code-token-key: #C4B5FD; + --md-code-token-string: #6EE7B7; + --md-code-token-number: #FBBF24; + --md-code-token-boolean: #F0ABFC; + --md-code-token-keyword: #93C5FD; + --md-code-token-function: #FDBA74; + --md-code-token-command: #A5B4FC; + --md-code-token-flag: #5EEAD4; + --md-code-token-punctuation: rgba(236, 231, 246, 0.56); +} + +/* 首屏主题预初始化(在 React 挂载前生效,减少 FOUC) */ +html.theme-dark body { + --md-sys-color-primary: #D0BCFF; + --md-sys-color-on-primary: #381E72; + --md-sys-color-primary-container: #4F378B; + --md-sys-color-on-primary-container: #EADDFF; + + --md-sys-color-surface: rgba(28, 27, 31, 0.8); + --md-sys-color-on-surface: #E6E1E5; + --md-sys-color-on-surface-variant: #CAC4D0; + --md-sys-color-surface-variant: #49454F; + + --md-sys-color-background: #141218; + --md-sys-color-outline-variant: rgba(208, 188, 255, 0.1); + + --glass-bg: rgba(28, 27, 31, 0.7); + --glass-border: rgba(208, 188, 255, 0.1); + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.4); + + --md-code-token-key: #C4B5FD; + --md-code-token-string: #6EE7B7; + --md-code-token-number: #FBBF24; + --md-code-token-boolean: #F0ABFC; + --md-code-token-keyword: #93C5FD; + --md-code-token-function: #FDBA74; + --md-code-token-command: #A5B4FC; + --md-code-token-flag: #5EEAD4; + --md-code-token-punctuation: rgba(236, 231, 246, 0.56); +} + +/* ===== 基础重置与全局交互层 ===== */ +/* 基础样式 */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +/* 仅在主题切换瞬间开启统一过渡,避免常驻 transition 拖慢滚动与重绘 */ +html.theme-transitioning body, +html.theme-transitioning .app, +html.theme-transitioning .sidebar, +html.theme-transitioning .main-content, +html.theme-transitioning .card, +html.theme-transitioning .config-card, +html.theme-transitioning .config-header, +html.theme-transitioning .top-bar::before, +html.theme-transitioning .nav-item, +html.theme-transitioning .btn, +html.theme-transitioning .btn-action, +html.theme-transitioning .sidebar-action-btn, +html.theme-transitioning .sidebar-github-inner, +html.theme-transitioning .header-clock-chip, +html.theme-transitioning .connection-chip, +html.theme-transitioning .status-timers-summary-pill, +html.theme-transitioning .status-timer-section-block, +html.theme-transitioning .status-timer-panel, +html.theme-transitioning .status-timer-info-item, +html.theme-transitioning .task-next-run-panel, +html.theme-transitioning input, +html.theme-transitioning select, +html.theme-transitioning textarea, +html.theme-transitioning button, +html.theme-transitioning .MuiPaper-root, +html.theme-transitioning .MuiAccordion-root, +html.theme-transitioning .MuiAccordionSummary-root, +html.theme-transitioning .MuiAccordionDetails-root, +html.theme-transitioning .MuiTypography-root, +html.theme-transitioning .MuiChip-root, +html.theme-transitioning .MuiButton-root, +html.theme-transitioning .MuiOutlinedInput-root, +html.theme-transitioning .MuiInputBase-input, +html.theme-transitioning .MuiSvgIcon-root { + transition: + background-color var(--theme-transition-duration) var(--theme-transition-easing), + color var(--theme-transition-duration) var(--theme-transition-easing), + border-color var(--theme-transition-duration) var(--theme-transition-easing), + fill var(--theme-transition-duration) var(--theme-transition-easing), + stroke var(--theme-transition-duration) var(--theme-transition-easing); +} + + +/* 移动端触摸优化 */ +html { + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; + touch-action: manipulation; +} + +body { + /* 背景使用多层径向渐变,营造轻量的 MD3 柔和氛围,而不是纯色平铺。 */ + background-color: var(--md-sys-color-background); + background-image: + radial-gradient(at 0% 0%, rgba(103, 80, 164, 0.08) 0, transparent 40%), + radial-gradient(at 50% 0%, rgba(236, 72, 153, 0.05) 0, transparent 40%), + radial-gradient(at 100% 0%, rgba(103, 80, 164, 0.08) 0, transparent 40%); + background-attachment: fixed; + color: var(--md-sys-color-on-surface); + font-family: 'Outfit', "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + min-height: 100vh; + overflow-x: hidden; +} + +/* 无障碍:仅供读屏器读取 */ +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* ===== 首屏启动层 ===== */ +/* 首屏加载页 */ +.boot-loader { + /* 启动层覆盖整个视口,在 React 尚未挂载时承担品牌露出与状态提示职责。 */ + position: fixed; + inset: 0; + z-index: 9999; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: #1C1B1F; + font-family: 'Outfit', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + background: + radial-gradient(ellipse at 20% 15%, rgba(103, 80, 164, 0.14), transparent 52%), + radial-gradient(ellipse at 80% 85%, rgba(103, 80, 164, 0.09), transparent 52%), + #FEF7FF; + opacity: 1; + transform: translateZ(0); + will-change: opacity, transform; +} + +html.theme-dark .boot-loader, +body.dark-theme .boot-loader { + color: #E6E1E5; + background: + radial-gradient(ellipse at 20% 15%, rgba(208, 188, 255, 0.13), transparent 52%), + radial-gradient(ellipse at 80% 85%, rgba(103, 80, 164, 0.18), transparent 52%), + #141218; +} + +#loading-skeleton.is-exiting { + opacity: 0; + transform: scale(0.985); + transition: opacity 0.3s ease-out, transform 0.3s ease-out; +} + +.boot-loader__logo { + /* Logo 外壳带轻微浮动动画,用于缓解静态等待时的“卡住感”。 */ + width: 80px; + height: 80px; + border-radius: 20px; + margin-bottom: 24px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(103, 80, 164, 0.08); + border: 1px solid rgba(103, 80, 164, 0.18); + box-shadow: 0 4px 24px rgba(103, 80, 164, 0.12); + overflow: hidden; + animation: boot-loader-float 2.4s ease-in-out infinite; + will-change: transform; +} + +.boot-loader__logo img { + width: 56px; + height: 56px; + object-fit: contain; + border-radius: 8px; +} + +html.theme-dark .boot-loader__logo, +body.dark-theme .boot-loader__logo { + background: rgba(208, 188, 255, 0.08); + border-color: rgba(208, 188, 255, 0.15); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35); +} + +.boot-loader__title { + font-size: 22px; + font-weight: 700; + margin-bottom: 8px; + letter-spacing: 0.01em; + color: #1C1B1F; +} + +html.theme-dark .boot-loader__title, +body.dark-theme .boot-loader__title { + color: #E6E1E5; +} + +.boot-loader__subtitle { + font-size: 13px; + color: #49454F; + margin-bottom: 28px; +} + +html.theme-dark .boot-loader__subtitle, +body.dark-theme .boot-loader__subtitle { + color: #CAC4D0; +} + +.boot-loader__progress { + /* 进度条不反映真实百分比,只承担“系统仍在工作”的感知反馈。 */ + width: min(260px, 68vw); + height: 3px; + border-radius: 999px; + overflow: hidden; + background: rgba(103, 80, 164, 0.12); + contain: paint; +} + +html.theme-dark .boot-loader__progress, +body.dark-theme .boot-loader__progress { + background: rgba(208, 188, 255, 0.12); +} + +.boot-loader__progress-bar { + height: 100%; + width: 42%; + border-radius: 999px; + background: linear-gradient(90deg, + rgba(103, 80, 164, 0.25) 0%, + rgba(103, 80, 164, 0.85) 50%, + rgba(103, 80, 164, 0.25) 100%); + animation: boot-loader-progress 1.45s ease-in-out infinite; + will-change: transform; + transform: translate3d(-110%, 0, 0) scaleX(0.35); +} + +html.theme-dark .boot-loader__progress-bar, +body.dark-theme .boot-loader__progress-bar { + background: linear-gradient(90deg, + rgba(208, 188, 255, 0.2) 0%, + rgba(208, 188, 255, 0.9) 50%, + rgba(208, 188, 255, 0.2) 100%); +} + +/* 首屏 logo 呼吸浮动动画。 */ +@keyframes boot-loader-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-7px); } +} + +/* 首屏进度条往返扫光动画。 */ +@keyframes boot-loader-progress { + 0% { transform: translate3d(-110%, 0, 0) scaleX(0.35); } + 50% { transform: translate3d(40%, 0, 0) scaleX(0.85); } + 100% { transform: translate3d(220%, 0, 0) scaleX(0.35); } +} + +.boot-login { + display: none; + width: min(360px, calc(100vw - 48px)); + margin: 16px auto 0; + text-align: left; +} + +.boot-login__label { + display: block; + margin-bottom: 8px; + font-size: 13px; + color: #625B71; +} + +.boot-login__input { + width: 100%; + box-sizing: border-box; + height: 44px; + padding: 0 14px; + border: 1px solid rgba(103, 80, 164, 0.22); + border-radius: 14px; + background: rgba(255, 255, 255, 0.88); + color: #1C1B1F; + outline: none; + font-size: 14px; + box-shadow: 0 8px 24px rgba(103, 80, 164, 0.08); +} + +.boot-login__input:focus { + border-color: rgba(103, 80, 164, 0.5); + box-shadow: 0 0 0 3px rgba(103, 80, 164, 0.12), 0 8px 24px rgba(103, 80, 164, 0.08); +} + +.boot-login__button { + width: 100%; + margin-top: 12px; + height: 44px; + border: none; + border-radius: 14px; + background: linear-gradient(135deg, #7F67BE 0%, #6750A4 100%); + color: #fff; + font-size: 14px; + font-weight: 600; + cursor: pointer; + box-shadow: 0 10px 24px rgba(103, 80, 164, 0.22); +} + +.boot-login__button:disabled { + cursor: not-allowed; +} + +html.theme-dark .boot-login__label, +body.dark-theme .boot-login__label { + color: #CAC4D0; +} + +html.theme-dark .boot-login__input, +body.dark-theme .boot-login__input { + background: rgba(28, 27, 31, 0.88); + color: #E6E1E5; + border-color: rgba(208, 188, 255, 0.22); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24); +} + +html.theme-dark .boot-login__input:focus, +body.dark-theme .boot-login__input:focus { + border-color: rgba(208, 188, 255, 0.48); + box-shadow: 0 0 0 3px rgba(208, 188, 255, 0.16), 0 8px 24px rgba(0, 0, 0, 0.24); +} + +html.theme-dark .boot-login__button, +body.dark-theme .boot-login__button { + background: linear-gradient(135deg, #A58CFF 0%, #7F67BE 100%); + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28); +} + +/* ===== 通用材质与布局骨架 ===== */ +/* 磨砂玻璃效果 */ +.glass { + /* 该类可供需要独立套用玻璃材质的区域复用。 */ + background: var(--glass-bg); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--glass-border); + box-shadow: var(--glass-shadow); +} + +/* 布局 */ +.app { + /* 整体采用“左侧导航 + 右侧主工作区”的桌面管理台结构。 */ + display: flex; + height: 100vh; + overflow: hidden; +} + +/* ===== 侧边栏导航区 ===== */ +/* 侧边栏 (左侧导航) */ +.sidebar { + /* 侧边栏固定宽度,使用玻璃材质与主内容区形成层次区隔。 */ + width: 280px; + height: 100%; + background: var(--glass-bg); + backdrop-filter: blur(24px); + border-right: 1px solid var(--glass-border); + display: flex; + flex-direction: column; + padding: 32px 16px; + z-index: 50; +} + +.sidebar-header { + padding: 0 16px 32px; + display: flex; + align-items: center; + gap: 12px; +} + +.sidebar-logo-img { + width: 48px; + height: 48px; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(103, 80, 164, 0.2); + object-fit: contain; +} + +.nav-item { + /* 单个导航项在 hover / active 状态下通过背景与透明度强化当前定位。 */ + padding: 14px 20px; + border-radius: 16px; + display: flex; + align-items: center; + gap: 16px; + cursor: pointer; + transition: + background-color var(--interactive-transition-duration) var(--theme-transition-easing), + color var(--interactive-transition-duration) var(--theme-transition-easing), + opacity var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing); + color: var(--md-sys-color-on-surface); + opacity: 0.8; + margin-bottom: 4px; +} + +.sidebar-notification-badge { + margin-left: auto; + min-width: 22px; + height: 22px; + padding: 0 7px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #FF6B6B, #E53935); + color: #FFFFFF; + font-size: 11px; + font-weight: 800; + line-height: 1; + letter-spacing: 0.01em; + box-shadow: 0 8px 18px rgba(229, 57, 53, 0.22); + border: 1px solid rgba(255, 255, 255, 0.42); + flex-shrink: 0; +} + +.nav-item.active .sidebar-notification-badge { + background: linear-gradient(135deg, #FF5A5F, #D32F2F); + box-shadow: 0 8px 18px rgba(211, 47, 47, 0.24); +} + +.nav-item:hover { + background: rgba(103, 80, 164, 0.08); + opacity: 1; +} + +.nav-item.active { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); + opacity: 1; + font-weight: 600; +} + +/* ===== 顶栏与主内容区 ===== */ +/* 主内容区域 */ +.main-wrapper { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +.top-bar { + /* 顶栏悬浮于内容区上方,使用伪元素实现半透明模糊遮罩。 */ + height: var(--top-bar-height); + padding: 0 40px; + display: flex; + align-items: center; + justify-content: space-between; + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 10; + background: transparent; + overflow: hidden; + isolation: isolate; +} + +.top-bar::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + background: rgba(255, 255, 255, 0.68); + -webkit-mask-image: linear-gradient(to bottom, black 55%, transparent 100%); + mask-image: linear-gradient(to bottom, black 55%, transparent 100%); + pointer-events: none; + z-index: 0; +} + +body.dark-theme .top-bar::before { + background: rgba(20, 18, 24, 0.88); +} + +.top-bar > * { + position: relative; + z-index: 1; +} + +.main-content { + /* 顶部留出 top-bar 高度,避免内容滚动时被悬浮顶栏遮挡。 */ + flex: 1; + overflow-y: auto; + padding: calc(var(--top-bar-height) + 16px) 24px 24px; + box-sizing: border-box; +} + +/* ===== 仪表盘卡片与通用容器 ===== */ +/* 仪表盘卡片 (Bento 风格) */ +.dashboard-grid { + /* 12 栏网格为桌面端默认布局基础,状态页和任务页都在此之上做跨度分配。 */ + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: 24px; +} + +.card { + /* card 是管理端最常见的视觉容器:统一圆角、边框、阴影和悬浮反馈。 */ + background: var(--md-sys-color-surface); + border-radius: var(--radius-lg); + padding: 24px; + border: 1px solid var(--glass-border); + box-shadow: var(--glass-shadow); + transition: + background-color var(--theme-transition-duration) var(--theme-transition-easing), + border-color var(--theme-transition-duration) var(--theme-transition-easing), + color var(--theme-transition-duration) var(--theme-transition-easing), + box-shadow var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing); +} + +/* 为前端的卡片提供统一的入场动画,减少页面首次渲染时的生硬感。 */ +.status-panel-card, +.tasks-summary-card, +.task-card-enhanced, +.status-timer-card, +.status-timer-section-block, +.tasks-empty-card, +.status-timers-empty-card, +.notifications-hero-card, +.notifications-empty-card, +.markdown-docs-sidebar-card, +.markdown-docs-content-card, +.markdown-docs-empty-card, +.markdown-docs-empty-side-card, +.config-card, +.config-header, +.config-renderer-shell, +.config-renderer-main, +.config-renderer-aside, +.config-renderer-form-card, +.config-renderer-preview-card, +.config-renderer-session-card, +.notifications-inline-meta-item, +.markdown-docs-file-item { + animation: card-fade-up 0.36s cubic-bezier(0.2, 0.8, 0.2, 1) both; +} + +.status-panel-card:nth-child(1), +.task-card-enhanced:nth-child(1), +.status-timer-card:nth-child(1), +.status-timer-section-block:nth-child(1) { + animation-delay: 0.02s; +} + +.status-panel-card:nth-child(2), +.task-card-enhanced:nth-child(2), +.status-timer-card:nth-child(2), +.status-timer-section-block:nth-child(2) { + animation-delay: 0.06s; +} + +.status-panel-card:nth-child(3), +.task-card-enhanced:nth-child(3), +.status-timer-card:nth-child(3), +.status-timer-section-block:nth-child(3) { + animation-delay: 0.1s; +} + +.status-panel-card:nth-child(n+4), +.task-card-enhanced:nth-child(n+4), +.status-timer-card:nth-child(n+4), +.status-timer-section-block:nth-child(n+4) { + animation-delay: 0.14s; +} + +.notifications-hero-card, +.config-header, +.markdown-docs-sidebar-card, +.config-renderer-aside { + animation-delay: 0.02s; +} + +.markdown-docs-content-card, +.config-renderer-main, +.config-renderer-form-card, +.config-renderer-session-card { + animation-delay: 0.08s; +} + +.config-card, +.config-renderer-shell, +.config-renderer-preview-card, +.notifications-empty-card, +.markdown-docs-empty-card, +.markdown-docs-empty-side-card { + animation-delay: 0.12s; +} + +.notifications-inline-meta-item:nth-child(1), +.markdown-docs-file-item:nth-child(1), +.config-card .MuiAccordion-root:nth-child(1), +.config-card .MuiPaper-root:nth-child(1) { + animation-delay: 0.03s; +} + +.notifications-inline-meta-item:nth-child(2), +.markdown-docs-file-item:nth-child(2), +.config-card .MuiAccordion-root:nth-child(2), +.config-card .MuiPaper-root:nth-child(2) { + animation-delay: 0.07s; +} + +.markdown-docs-file-item:nth-child(n+3), +.config-card .MuiAccordion-root:nth-child(n+3), +.config-card .MuiPaper-root:nth-child(n+3) { + animation-delay: 0.11s; +} + +/* ===== 配置页容器 ===== */ +/* 配置页面专用卡片样式 - 固定高度,内部滚动 */ +.config-card { + /* 配置页需要长表单滚动,因此外层卡片固定高度、内部区域自行滚动。 */ + height: calc(100vh - var(--top-bar-height) - 40px); /* 视口高度减去顶部栏和底部边距 */ + display: flex; + flex-direction: column; + padding: 0 !important; /*以此接管padding控制*/ + overflow: hidden; +} + +.config-header { + /* 配置头部固定在卡片顶部,确保模式切换、提示信息和标题不随表单滚走。 */ + padding: 20px 24px; + flex-shrink: 0; + border-bottom: 1px solid var(--glass-border); + background: rgba(255, 255, 255, 0.5); + backdrop-filter: blur(8px); + z-index: 10; +} + +body.dark-theme .config-header { + background: rgba(20, 18, 24, 0.8); /* 增加不透明度,减弱透底亮度 */ + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 30px 0 rgba(103, 80, 164, 0.12); +} + +/* Bento 跨列:供各页面按 12 栏系统快速声明卡片宽度。 */ +.span-4 { grid-column: span 4; } +.span-8 { grid-column: span 8; } +.span-12 { grid-column: span 12; } + +/* 状态徽章 */ +.badge { + padding: 4px 12px; + border-radius: 99px; + font-size: 12px; + font-weight: 600; +} + +.badge-success { background: #dcfce7; color: #166534; } +.badge-error { background: #fee2e2; color: #991b1b; } + +/* ===== 通用按钮与交互控件 ===== */ +/* 按钮 */ +.btn { + padding: 10px 20px; + border-radius: 12px; + border: none; + font-weight: 600; + cursor: pointer; + transition: + background-color var(--interactive-transition-duration) var(--theme-transition-easing), + border-color var(--interactive-transition-duration) var(--theme-transition-easing), + color var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing), + box-shadow var(--interactive-transition-duration) var(--theme-transition-easing), + opacity var(--interactive-transition-duration) var(--theme-transition-easing); +} + +.btn-primary { + background: var(--md-sys-color-primary); + color: white; + box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); +} + +.btn-primary:hover { + transform: translateY(-1px); + box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4); +} + +/* 通用操作按钮样式 */ +.btn-action { + width: 100%; + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface); + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + font-weight: 600; + border: 1px solid transparent; + transition: + background-color var(--interactive-transition-duration) var(--theme-transition-easing), + color var(--interactive-transition-duration) var(--theme-transition-easing), + border-color var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing), + box-shadow var(--interactive-transition-duration) var(--theme-transition-easing), + opacity var(--interactive-transition-duration) var(--theme-transition-easing); + padding: 12px 16px; + line-height: 1.5; + cursor: pointer; +} + +.btn-action span { + display: inline-flex; + align-items: center; + justify-content: center; +} + +.btn-action:hover { + background: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); + border-color: var(--md-sys-color-outline-variant); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); +} + +.btn-action:active { + transform: translateY(0); + box-shadow: none; + opacity: 0.8; +} + +/* 暗色模式下的按钮覆盖样式 */ +body.dark-theme .btn-action { + background: rgba(255, 255, 255, 0.08); + color: var(--md-sys-color-on-surface); + border: 1px solid rgba(255, 255, 255, 0.05); +} + +body.dark-theme .btn-action:hover { + background: rgba(208, 188, 255, 0.15); + color: var(--md-sys-color-primary); + border-color: rgba(208, 188, 255, 0.3); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); +} + +/* ===== 滚动行为与兼容辅助类 ===== */ +/* 滚动条 */ +::-webkit-scrollbar { width: 6px; height: 6px; } +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { + background: var(--md-sys-color-secondary-container); + border-radius: 10px; +} +::-webkit-scrollbar-thumb:hover { background: var(--md-sys-color-secondary); } + +/* 移动端滚动优化 */ +.sidebar > div:nth-child(2), +.main-content { + -webkit-overflow-scrolling: touch; + scroll-behavior: smooth; +} + +/* 按钮点击反馈 */ +.nav-item, +.btn, +.btn-action { + -webkit-tap-highlight-color: transparent; + user-select: none; +} + +.nav-item:active, +.btn:active { + opacity: 0.7; + transition: opacity 0.12s var(--theme-transition-easing); +} + +/* 兼容之前的组件辅助样式 */ +.mono { + font-family: "Consolas", "SFMono-Regular", monospace; + font-size: 12px; +} +.grid { + display: grid; + gap: 14px; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); +} + +/* ===== 顶栏组件样式:时钟、连接状态、侧栏操作区 ===== */ +.header-clock-chip { + font-size: 14px; + font-weight: 700; + color: var(--md-sys-color-primary); + background: var(--md-sys-color-surface-variant); + padding: 6px 14px; + border-radius: 12px; + border: 1px solid var(--md-sys-color-outline-variant); + box-shadow: 0 2px 4px rgba(0,0,0,0.05); + display: flex; + align-items: center; + gap: 8px; +} + +.header-clock-label { + font-size: 14px; + opacity: 0.8; + font-weight: 600; +} + +.header-clock-value { + font-family: Monaco, Consolas, "Courier New", monospace; + font-size: 15px; + font-weight: 800; + letter-spacing: 0.5px; +} + +.connection-chip { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 16px; + border-radius: 12px; + border: 1px solid transparent; +} + +.connection-chip.is-connected { + background: rgba(76, 175, 80, 0.1); + border-color: rgba(76, 175, 80, 0.2); +} + +.connection-chip.is-disconnected { + background: rgba(244, 67, 54, 0.1); + border-color: rgba(244, 67, 54, 0.2); +} + +.connection-chip-dot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.connection-chip.is-connected .connection-chip-dot { + background: #4CAF50; + box-shadow: 0 0 8px #4CAF50; +} + +.connection-chip.is-disconnected .connection-chip-dot { + background: #F44336; + box-shadow: 0 0 8px #F44336; +} + +.connection-chip-text { + font-weight: 600 !important; + font-size: 13px !important; +} + +.connection-chip.is-connected .connection-chip-text { + color: #4CAF50; +} + +.connection-chip.is-disconnected .connection-chip-text { + color: #F44336; +} + +.sidebar-actions-panel { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 16px; +} + +.sidebar-action-btn { + width: 100%; + margin-bottom: 0; + padding: 10px; + font-size: 13px; + font-weight: 600; + background: rgba(0, 0, 0, 0.05); + color: var(--md-sys-color-on-surface); + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + border: 1px solid rgba(0, 0, 0, 0.04); + border-radius: 12px; + box-shadow: none; + line-height: 1.2; +} + +.sidebar-action-btn:hover { + transform: none; + box-shadow: none; + background: rgba(103, 80, 164, 0.08); + color: var(--md-sys-color-on-surface); +} + +body.dark-theme .sidebar-action-btn { + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.05); +} + +body.dark-theme .sidebar-action-btn:hover { + background: rgba(255, 255, 255, 0.12); + color: var(--md-sys-color-on-surface); +} + +.sidebar-github-card { + display: block; + color: var(--md-sys-color-on-surface); +} + +.sidebar-github-inner { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background: rgba(0, 0, 0, 0.03); + border-radius: 12px; + transition: + background-color var(--interactive-transition-duration) var(--theme-transition-easing), + border-color var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing), + box-shadow var(--interactive-transition-duration) var(--theme-transition-easing); + border: 1px solid rgba(0, 0, 0, 0.06); + cursor: pointer; +} + +body.dark-theme .sidebar-github-inner { + background: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.05); +} + +.sidebar-github-inner:hover { + transform: translateY(-1px); + box-shadow: var(--glass-shadow); +} + +.sidebar-github-texts { + display: flex; + flex-direction: column; + gap: 3px; + overflow: hidden; +} + +.sidebar-github-author { + font-weight: 700 !important; + color: inherit !important; + font-size: 0.75rem !important; + line-height: 1.2 !important; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + max-width: 170px; +} + +.sidebar-github-meta { + display: block !important; + opacity: 0.6; + line-height: 1.2 !important; + font-size: 0.7rem !important; + color: inherit !important; +} + +/* ===== 状态页:概览卡片与计时器可视化 ===== */ +.proactive-status-grid { + align-items: stretch; +} + +.status-panel-card { + height: 100%; + display: flex; + flex-direction: column; + min-height: 332px; +} + +.status-panel-card-primary { + background: + linear-gradient(180deg, rgba(59, 130, 246, 0.06), transparent 50%), + var(--md-sys-color-surface); +} + +.status-panel-icon { + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; +} + +.status-panel-icon-blue { background: rgba(59, 130, 246, 0.1); } +.status-panel-icon-purple { background: rgba(168, 85, 247, 0.12); } +.status-panel-icon-pink { background: rgba(236, 72, 153, 0.1); } + +.status-metric-list { + display: flex; + flex-direction: column; + gap: 14px; + flex: 1; + justify-content: center; +} + +.status-metric-row { + display: flex; + justify-content: space-between; + gap: 16px; + align-items: center; +} + +.status-metric-label { + opacity: 0.72; + font-weight: 500 !important; +} + +.status-metric-value { + font-weight: 600 !important; + text-align: right; +} + +.status-metric-value.is-emphasize { + font-weight: 800 !important; +} + +.status-metric-value.is-success { + color: #2E7D32; +} + +.status-metric-value.is-error { + color: #C62828; +} + +.status-divider { + height: 1px; + background: var(--md-sys-color-outline-variant); +} + +.status-actions-list { + display: flex; + flex-direction: column; + gap: 16px; + flex: 1; + justify-content: center; +} + +.status-action-tooltip-wrap { + position: relative; + width: 100%; +} + +.status-action-tooltip-bubble { + position: absolute; + left: 50%; + bottom: calc(100% + 10px); + transform: translateX(-50%) translateY(6px); + width: max-content; + max-width: min(92vw, 460px); + padding: 10px 14px; + border-radius: 12px; + background: rgba(28, 27, 31, 0.92); + color: #fff; + font-size: 12px; + line-height: 1.5; + text-align: center; + white-space: normal; + word-break: keep-all; + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22); + border: 1px solid rgba(255, 255, 255, 0.08); + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease; + z-index: 12; +} + +.status-action-tooltip-bubble::after { + content: ''; + position: absolute; + left: 50%; + top: 100%; + transform: translateX(-50%); + border-width: 6px 6px 0 6px; + border-style: solid; + border-color: rgba(28, 27, 31, 0.92) transparent transparent transparent; +} + +.status-action-tooltip-wrap:hover .status-action-tooltip-bubble, +.status-action-tooltip-wrap:focus-within .status-action-tooltip-bubble { + opacity: 1; + visibility: visible; + transform: translateX(-50%) translateY(0); +} + +body.dark-theme .status-action-tooltip-bubble { + background: rgba(245, 244, 248, 0.96); + color: #1C1B1F; + border-color: rgba(28, 27, 31, 0.08); +} + +body.dark-theme .status-action-tooltip-bubble::after { + border-color: rgba(245, 244, 248, 0.96) transparent transparent transparent; +} + + +.status-timers-section { + /* 计时器区域采用“分区 + 卡片网格”的结构,便于区分群沉默与自动触发。 */ + display: flex; + flex-direction: column; + gap: 18px; + padding: 20px; + border-radius: calc(var(--radius-lg) + 2px); + border: 1px solid var(--glass-border); + background: + linear-gradient(180deg, rgba(103, 80, 164, 0.05), rgba(103, 80, 164, 0) 34%), + rgba(255, 255, 255, 0.3); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); +} + +body.dark-theme .status-timers-section { + background: + linear-gradient(180deg, rgba(208, 188, 255, 0.08), rgba(208, 188, 255, 0) 34%), + rgba(20, 18, 24, 0.36); + border-color: rgba(208, 188, 255, 0.12); +} + +.status-timers-header-row { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 18px; + padding-bottom: 4px; +} + +.status-timers-summary-pills { + display: flex; + gap: 10px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.status-timers-summary-pill { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + border-radius: 999px; + border: 1px solid var(--glass-border); + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface-variant); +} + +.status-timers-summary-pill-label { + font-size: 12px; + font-weight: 600; +} + +.status-timers-summary-pill-count { + min-width: 18px; + height: 18px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 6px; + font-size: 11px; + font-weight: 800; + color: var(--md-sys-color-on-primary-container); + background: var(--md-sys-color-primary-container); +} + +.status-timer-sections { + display: flex; + flex-direction: column; + gap: 20px; +} + +.status-timer-section-block { + display: flex; + flex-direction: column; + gap: 14px; + padding: 16px; + border-radius: 18px; + background: rgba(255, 255, 255, 0.34); + border: 1px solid rgba(103, 80, 164, 0.12); +} + +body.dark-theme .status-timer-section-block { + background: rgba(255, 255, 255, 0.02); + border-color: rgba(208, 188, 255, 0.14); +} + +.status-timer-section-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + padding-bottom: 10px; + border-bottom: 1px dashed rgba(103, 80, 164, 0.14); +} + +body.dark-theme .status-timer-section-head { + border-bottom-color: rgba(208, 188, 255, 0.16); +} + +.status-timer-section-head-main { + min-width: 0; +} + +.status-timer-section-title { + font-weight: 800 !important; +} + +.status-timer-section-desc { + opacity: 0.72; + margin-top: 4px !important; +} + +.status-timer-section-count { + flex-shrink: 0; + padding: 6px 12px; + border-radius: 999px; + background: var(--md-sys-color-surface-variant); + border: 1px solid var(--glass-border); + color: var(--md-sys-color-primary); + font-size: 12px; + font-weight: 800; +} + +.status-timer-section-body { + padding-top: 2px; +} + +.status-timer-section-empty { + padding: 14px 16px; + border-radius: 14px; + background: rgba(103, 80, 164, 0.05); + border: 1px dashed rgba(103, 80, 164, 0.18); + color: var(--md-sys-color-on-surface-variant); + font-size: 13px; +} + +.status-timers-empty-card { + text-align: center; + padding: 44px 24px; +} + +.status-timers-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); + gap: 18px; +} + +.status-timer-card { + /* 单张计时器卡片负责展示一个会话当前最值得关注的计时状态。 */ + display: flex; + flex-direction: column; + gap: 14px; + min-height: 312px; + position: relative; + overflow: hidden; + isolation: isolate; + border-top: 4px solid transparent; +} + + +.status-timer-card.accent-group-silence { + border-top-color: #2563EB; + background: + linear-gradient(180deg, rgba(59, 130, 246, 0.06), transparent 30%), + var(--md-sys-color-surface); +} + +.status-timer-card.accent-auto-group { + border-top-color: #8B5CF6; + background: + linear-gradient(180deg, rgba(139, 92, 246, 0.07), transparent 30%), + var(--md-sys-color-surface); +} + +.status-timer-card.accent-auto-friend { + border-top-color: #EC4899; + background: + linear-gradient(180deg, rgba(236, 72, 153, 0.07), transparent 30%), + var(--md-sys-color-surface); +} + +.status-timer-card.is-urgent { + border-color: rgba(245, 158, 11, 0.35); +} + +.status-timer-card.is-expired { + border-color: rgba(244, 67, 54, 0.28); +} + +.status-timer-card-top { + display: grid; + grid-template-columns: minmax(220px, 1fr) auto; + gap: 14px; + align-items: flex-start; +} + +.status-timer-title-block { + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; +} + +.status-timer-kicker { + display: block !important; + margin-bottom: 4px !important; + opacity: 0.6; +} + +.status-timer-session { + font-weight: 700 !important; + word-break: break-all; + font-size: 13px !important; + line-height: 1.4; +} + +.status-timer-session.is-primary { + font-size: 28px !important; + letter-spacing: -0.2px; + line-height: 1.25; +} + +.status-timer-session-sub { + display: block !important; + max-width: 100%; + white-space: nowrap; + word-break: normal; + overflow: visible; + text-overflow: clip; + opacity: 0.72; + font-size: 12px !important; +} + +.status-timer-chip-stack { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + align-items: center; + gap: 8px; + max-width: 280px; + flex-shrink: 0; +} + +.status-timer-kind-badge { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 4px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 800; + white-space: nowrap; + border: 1px solid transparent; + background: rgba(103, 80, 164, 0.08); + color: var(--md-sys-color-primary); +} + +.status-timer-kind-badge.accent-group-silence { + background: rgba(59, 130, 246, 0.12); + color: #2563EB; + border-color: rgba(59, 130, 246, 0.18); +} + +.status-timer-kind-badge.accent-auto-group { + background: rgba(139, 92, 246, 0.12); + color: #7C3AED; + border-color: rgba(139, 92, 246, 0.18); +} + +.status-timer-kind-badge.accent-auto-friend { + background: rgba(236, 72, 153, 0.12); + color: #DB2777; + border-color: rgba(236, 72, 153, 0.18); +} + +.status-timer-meta-row { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + flex-wrap: wrap; +} + +.status-timer-category-pill { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; + white-space: nowrap; + background: rgba(103, 80, 164, 0.08); + color: var(--md-sys-color-primary); +} + +.status-timer-reset-hint { + /* 当群沉默计时器被新消息重置时,短暂显示顶部提示帮助用户理解时间跳变。 */ + position: absolute; + top: 12px; + left: 16px; + right: 16px; + z-index: 3; + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 999px; + background: rgba(240, 246, 255, 0.84); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(59, 130, 246, 0.18); + box-shadow: 0 8px 18px rgba(59, 130, 246, 0.12); + color: #2563EB; + font-size: 11px; + font-weight: 700; + line-height: 1.4; + pointer-events: none; + opacity: 0; + transform: translateY(-6px) scale(0.98); + animation: status-reset-hint-fade 4.2s ease-out forwards; +} + +body.dark-theme .status-timer-reset-hint { + background: rgba(30, 58, 138, 0.95); + border-color: rgba(59, 130, 246, 0.4); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + color: #93C5FD; +} + +.status-timer-reset-dot { + width: 7px; + height: 7px; + border-radius: 999px; + background: #2563EB; + box-shadow: 0 0 0 rgba(37, 99, 235, 0.35); + animation: status-reset-dot-pulse 1.4s ease-out infinite; + flex-shrink: 0; +} + +.status-timer-card.is-resetting { + border-color: rgba(59, 130, 246, 0.28); + box-shadow: 0 12px 32px rgba(59, 130, 246, 0.12); +} + +.status-timer-panel { + background: var(--md-sys-color-surface-variant); + padding: 16px; + border-radius: 16px; + border: 1px solid rgba(103, 80, 164, 0.08); + display: flex; + flex-direction: column; + gap: 10px; +} + +.status-timer-primary-row { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: flex-start; +} + +.status-timer-label { + display: block !important; + margin-bottom: 4px !important; + opacity: 0.68; +} + +.status-timer-countdown { + font-weight: 800 !important; + color: var(--md-sys-color-primary); +} + +.status-timer-progress-value { + flex-shrink: 0; + font-size: 13px; + font-weight: 800; + color: var(--md-sys-color-on-surface-variant); +} + +.status-timer-info-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.status-timer-info-item { + padding: 14px; + border-radius: 14px; + background: rgba(255,255,255,0.42); + border: 1px solid var(--glass-border); +} + +body.dark-theme .status-timer-info-item { + background: rgba(255,255,255,0.04); +} + +.status-timer-info-label { + display: block !important; + margin-bottom: 6px !important; + opacity: 0.68; +} + +.status-timer-info-value { + font-weight: 600 !important; + line-height: 1.45 !important; + word-break: break-word; + white-space: pre-wrap; +} + +.status-timer-info-value.is-emphasize { + font-weight: 800 !important; +} + +/* ===== 任务页:任务卡片与调度进度 ===== */ +.tasks-header-row { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + margin-bottom: 20px; +} + +.tasks-header-subtitle { + opacity: 0.72; +} + +.tasks-empty-card { + text-align: center; + padding: 48px 24px; +} + +.tasks-empty-icon { + font-size: 34px; + margin-bottom: 12px; +} + +.notifications-view { + display: flex; + flex-direction: column; + gap: 18px; +} + +.notifications-hero-card { + padding: 18px 24px; + background: + linear-gradient(135deg, rgba(103, 80, 164, 0.08), rgba(103, 80, 164, 0.025) 48%, rgba(255, 255, 255, 0.72) 100%), + var(--md-sys-color-surface); + position: relative; + overflow: hidden; +} + +.notifications-hero-card::after { + content: ''; + position: absolute; + inset: auto -40px -56px auto; + width: 180px; + height: 180px; + border-radius: 50%; + background: radial-gradient(circle, rgba(103, 80, 164, 0.14), transparent 68%); + pointer-events: none; +} + +.notifications-header-row { + margin-bottom: 0; + align-items: flex-start; + position: relative; + z-index: 1; +} + +.notifications-header-main { + min-width: 0; + flex: 1; +} + +.notifications-title-row { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 14px 18px; + flex-wrap: wrap; + margin-bottom: 6px; +} + +.notifications-inline-meta { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 8px 10px; + margin-top: 0; + max-width: none; +} + +.notifications-inline-meta-item { + min-width: auto; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 0; + border-radius: 0; + background: transparent; + border: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +.notifications-inline-meta-item.is-pill { + min-height: 34px; + padding: 0 12px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.58); + border: 1px solid rgba(103, 80, 164, 0.12); + box-shadow: none; +} + +.notifications-inline-meta-item strong { + font-size: 12px; + line-height: 1.2; + font-weight: 700; + color: var(--md-sys-color-on-surface); + word-break: break-word; +} + +.notifications-inline-meta-label { + font-size: 12px; + font-weight: 700; + color: var(--md-sys-color-on-surface-variant); + white-space: nowrap; +} + +.notifications-actions-row { + display: flex; + align-items: flex-start; + justify-content: flex-end; + gap: 10px; + flex-wrap: wrap; + flex-shrink: 0; + padding-left: 12px; + padding-top: 2px; +} + +.notifications-actions-row .MuiButton-root { + min-height: 52px; + padding: 0 22px; + border-radius: 18px; + font-weight: 700; + box-shadow: none; + border-width: 1px; +} + +.notifications-empty-card { + padding: 56px 28px; +} + +.notifications-feed-list { + display: flex; + flex-direction: column; + gap: 16px; +} + +.markdown-docs-shell { + display: grid; + grid-template-columns: minmax(260px, 320px) minmax(0, 1fr); + gap: 18px; + align-items: start; +} + +.markdown-docs-aside-column { + min-width: 0; +} + +.markdown-docs-aside-sticky { + position: sticky; + top: calc(var(--top-bar-height) + 16px); +} + +.markdown-docs-sidebar-card, +.markdown-docs-content-card { + min-height: 520px; +} + +.markdown-docs-sidebar-card { + display: flex; + flex-direction: column; + gap: 16px; + height: calc(100vh - var(--top-bar-height) - 32px); + max-height: 880px; + overflow: hidden; +} + +.markdown-docs-sidebar-head { + display: flex; + flex-direction: column; + gap: 4px; + flex-shrink: 0; +} + +.markdown-docs-file-list { + display: flex; + flex-direction: column; + gap: 10px; + min-height: 0; + flex: 1; + overflow-y: auto; + padding-right: 4px; +} + +.markdown-docs-content-column { + min-width: 0; +} + +.markdown-docs-file-item { + width: 100%; + text-align: left; + padding: 14px 16px; + border-radius: 16px; + border: 1px solid rgba(103, 80, 164, 0.12); + background: rgba(255, 255, 255, 0.56); + color: var(--md-sys-color-on-surface); + cursor: pointer; + transition: + background-color var(--interactive-transition-duration) var(--theme-transition-easing), + border-color var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing), + box-shadow var(--interactive-transition-duration) var(--theme-transition-easing); +} + +.markdown-docs-file-item:hover { + transform: translateY(-2px); + background: rgba(103, 80, 164, 0.08); + border-color: rgba(103, 80, 164, 0.18); + box-shadow: 0 8px 22px rgba(103, 80, 164, 0.08); +} + +.markdown-docs-file-item.is-active { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); + border-color: rgba(103, 80, 164, 0.22); +} + +.markdown-docs-file-item-top { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 6px; +} + +.markdown-docs-file-icon { + flex-shrink: 0; +} + +.markdown-docs-file-title { + font-size: 14px; + font-weight: 800; + line-height: 1.35; + word-break: break-word; +} + +.markdown-docs-file-path { + opacity: 0.72; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.markdown-docs-content-card { + display: flex; + flex-direction: column; + gap: 18px; +} + +.markdown-docs-article-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + padding-bottom: 12px; + border-bottom: 1px solid var(--glass-border); +} + +.markdown-docs-article { + min-height: 380px; +} + +.markdown-docs-empty-card, +.markdown-docs-empty-side-card { + min-height: 240px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.tasks-grid-enhanced { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); + gap: 18px; +} + +.task-card-enhanced { + /* 任务卡片与状态页卡片风格保持一致,但更强调执行时间与操作按钮区域。 */ + display: flex; + flex-direction: column; + gap: 16px; + min-height: 320px; + position: relative; + overflow: hidden; +} + +.notification-feed-card { + min-height: auto; + padding: 18px 20px 16px; + gap: 12px; + border-radius: 20px; + background: + linear-gradient(180deg, rgba(103, 80, 164, 0.06), rgba(103, 80, 164, 0.02) 34%, rgba(255, 255, 255, 0.78) 100%), + var(--md-sys-color-surface); + box-shadow: 0 8px 24px rgba(103, 80, 164, 0.08); +} + +.notification-feed-card::after { + width: 140px; + height: 140px; + inset: auto -34px -46px auto; + background: radial-gradient(circle, rgba(103, 80, 164, 0.12), transparent 70%); +} + +.notification-feed-card.is-read { + border-color: rgba(103, 80, 164, 0.10); +} + +.notification-feed-card-top { + align-items: center; + justify-content: space-between; + gap: 14px; + padding-bottom: 2px; +} + +.notification-feed-heading-group { + min-width: 0; + flex: 1; + display: inline-flex; + align-items: center; + gap: 10px; + flex-wrap: nowrap; +} + +.notification-feed-title-block { + gap: 6px; +} + +.notification-feed-meta-row { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.notification-feed-time { + opacity: 0.78; + font-size: 11.5px !important; +} + +.notification-feed-title { + flex: 0 1 auto; + min-width: 0; + font-size: 1.22rem !important; + line-height: 1.35 !important; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.notification-feed-side { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + padding-top: 1px; +} + +.notification-feed-meta-inline { + flex-wrap: wrap; + justify-content: flex-end; + padding-left: 12px; +} + +.notification-feed-heading-group .MuiChip-root, +.notification-feed-type-chip { + height: 30px; + border-radius: 999px; + font-weight: 700; + flex-shrink: 0; + margin-left: 0; +} + +.notification-feed-status-pill { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 28px; + padding: 0 12px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; + border: 1px solid transparent; + white-space: nowrap; +} + +.notification-feed-status-pill.is-read { + color: #5B6472; + background: rgba(91, 100, 114, 0.10); + border-color: rgba(91, 100, 114, 0.14); +} + +.notification-feed-status-pill.is-unread { + color: #B45309; + background: rgba(245, 158, 11, 0.14); + border-color: rgba(245, 158, 11, 0.22); +} + + +.task-card-enhanced.is-urgent { + border-color: rgba(245, 158, 11, 0.35); +} + +.task-card-enhanced.is-expired { + border-color: rgba(244, 67, 54, 0.28); +} + +.task-card-top { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: flex-start; +} + +.task-card-title-block { + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; +} + +.task-card-kicker { + display: block !important; + margin-bottom: 4px !important; + opacity: 0.6; +} + +.task-card-session { + font-weight: 700 !important; + word-break: break-all; + font-size: 13px !important; + line-height: 1.4; +} + +.task-card-session.is-primary { + font-size: 28px !important; + letter-spacing: -0.2px; + line-height: 1.25; +} + +.task-card-session-sub { + display: block !important; + max-width: none; + white-space: nowrap; + overflow: visible; + text-overflow: clip; + opacity: 0.72; + font-size: 12px !important; +} + +.task-next-run-panel { + background: var(--md-sys-color-surface-variant); + padding: 16px; + border-radius: 16px; + border: 1px solid rgba(103, 80, 164, 0.08); + display: flex; + flex-direction: column; + gap: 10px; +} + +.notification-feed-content-panel { + position: relative; + padding: 16px 16px 56px; + background: linear-gradient(180deg, rgba(103, 80, 164, 0.08), rgba(103, 80, 164, 0.035)); + border-color: rgba(103, 80, 164, 0.09); + border-radius: 16px; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45); +} + +.notification-feed-content-text { + color: var(--md-sys-color-on-surface); +} + +.notification-feed-content-text-plain { + white-space: pre-wrap; +} + +.notification-md { + color: var(--md-sys-color-on-surface); + line-height: 1.82; + font-size: 0.97rem; + word-break: break-word; +} + +.notification-md > :first-child { + margin-top: 0; +} + +.notification-md > :last-child { + margin-bottom: 0; +} + +.notification-md p, +.notification-md ul, +.notification-md ol, +.notification-md blockquote, +.notification-md pre, +.notification-md .notification-md-table-wrap, +.notification-md .notification-md-details, +.notification-md .notification-md-mermaid-block { + margin: 0 0 0.95rem; +} + +.notification-md h1, +.notification-md h2, +.notification-md h3, +.notification-md h4, +.notification-md h5, +.notification-md h6 { + margin: 1.2rem 0 0.72rem; + color: var(--md-sys-color-on-surface); + font-weight: 800; + letter-spacing: -0.01em; + line-height: 1.32; +} + +.notification-md h1 { + font-size: 1.32rem; + padding-bottom: 0.46rem; + border-bottom: 1px solid rgba(103, 80, 164, 0.16); +} + +.notification-md h2 { + font-size: 1.18rem; + padding-left: 0.72rem; + border-left: 4px solid var(--md-sys-color-primary); +} + +.notification-md h3 { + font-size: 1.05rem; + color: var(--md-sys-color-primary); +} + +.notification-md h4, +.notification-md h5, +.notification-md h6 { + font-size: 0.98rem; +} + +.notification-md p { + color: var(--md-sys-color-on-surface); +} + +.notification-md strong { + color: var(--md-sys-color-on-surface); + font-weight: 800; +} + +.notification-md em, +.notification-md del { + color: var(--md-sys-color-on-surface-variant); +} + +.notification-md a { + color: var(--md-sys-color-primary); + text-decoration: none; + font-weight: 700; + border-bottom: 1px solid rgba(103, 80, 164, 0.22); + transition: + color var(--interactive-transition-duration) var(--theme-transition-easing), + border-color var(--interactive-transition-duration) var(--theme-transition-easing), + opacity var(--interactive-transition-duration) var(--theme-transition-easing); +} + +.notification-md a:hover { + opacity: 0.88; + border-bottom-color: rgba(103, 80, 164, 0.42); +} + +.notification-md ul, +.notification-md ol { + padding-left: 1.35rem; +} + +.notification-md li + li { + margin-top: 0.34rem; +} + +.notification-md li::marker { + color: var(--md-sys-color-primary); + font-weight: 700; +} + +.notification-md hr { + border: 0; + height: 1px; + margin: 1rem 0; + background: linear-gradient( + 90deg, + transparent, + rgba(103, 80, 164, 0.24), + transparent + ); +} + +.notification-md blockquote { + margin-left: 0; + margin-right: 0; + padding: 0.9rem 1rem; + border-left: 4px solid var(--md-sys-color-primary); + border-radius: 0 var(--radius-md) var(--radius-md) 0; + background: linear-gradient(135deg, rgba(103, 80, 164, 0.12), rgba(103, 80, 164, 0.05)); + color: var(--md-sys-color-on-surface); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24); +} + +.notification-md blockquote p { + margin: 0; +} + +.notification-md .notification-md-callout { + position: relative; + padding: 1rem 1.1rem 1rem 1.15rem; + border-left-width: 5px; + border-radius: 0 var(--radius-md) var(--radius-md) 0; + background: linear-gradient(135deg, rgba(103, 80, 164, 0.08), rgba(103, 80, 164, 0.03)); +} + +.notification-md .notification-md-callout-header { + display: inline-flex; + align-items: center; + gap: 0.55rem; + margin-bottom: 0.72rem; + font-weight: 800; + line-height: 1.2; +} + +.notification-md .notification-md-callout-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.2rem; + height: 1.2rem; + font-size: 0.95rem; +} + +.notification-md .notification-md-callout-title { + font-size: 1rem; + letter-spacing: -0.01em; +} + +.notification-md .notification-md-callout.is-note { + border-left-color: #2563EB; + background: linear-gradient(135deg, rgba(37, 99, 235, 0.12), rgba(37, 99, 235, 0.04)); +} + +.notification-md .notification-md-callout.is-note .notification-md-callout-header { + color: #2563EB; +} + +.notification-md .notification-md-callout.is-tip { + border-left-color: #0F9D58; + background: linear-gradient(135deg, rgba(15, 157, 88, 0.12), rgba(15, 157, 88, 0.04)); +} + +.notification-md .notification-md-callout.is-tip .notification-md-callout-header { + color: #0F9D58; +} + +.notification-md .notification-md-callout.is-important { + border-left-color: #7C3AED; + background: linear-gradient(135deg, rgba(124, 58, 237, 0.14), rgba(124, 58, 237, 0.05)); +} + +.notification-md .notification-md-callout.is-important .notification-md-callout-header { + color: #7C3AED; +} + +.notification-md .notification-md-callout.is-warning { + border-left-color: #D97706; + background: linear-gradient(135deg, rgba(217, 119, 6, 0.14), rgba(217, 119, 6, 0.05)); +} + +.notification-md .notification-md-callout.is-warning .notification-md-callout-header { + color: #D97706; +} + +.notification-md .notification-md-callout.is-caution { + border-left-color: #DC2626; + background: linear-gradient(135deg, rgba(220, 38, 38, 0.14), rgba(220, 38, 38, 0.05)); +} + +.notification-md .notification-md-callout.is-caution .notification-md-callout-header { + color: #DC2626; +} + +.notification-md .notification-md-callout > :last-child { + margin-bottom: 0; +} + +.notification-md :not(pre) > code { + display: inline-block; + padding: 0.14rem 0.48rem; + margin: 0 0.1rem; + border-radius: 999px; + border: 1px solid rgba(103, 80, 164, 0.16); + background: rgba(103, 80, 164, 0.10); + color: var(--md-sys-color-primary); + font-family: "Consolas", "SFMono-Regular", monospace; + font-size: 0.9em; + font-weight: 700; + line-height: 1.45; +} + +.notification-md-code-block, +.notification-md-mermaid-block { + margin: 0 0 0.95rem; + overflow: hidden; + border-radius: 18px; + border: 1px solid rgba(103, 80, 164, 0.18); + background: + linear-gradient(180deg, rgba(103, 80, 164, 0.18), rgba(103, 80, 164, 0.05) 18%, rgba(255, 255, 255, 0.94) 100%), + rgba(255, 255, 255, 0.92); + box-shadow: + 0 10px 28px rgba(103, 80, 164, 0.12), + inset 0 1px 0 rgba(255, 255, 255, 0.65); +} + +.notification-md-code-header { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 42px; + padding: 0 14px; + border-bottom: 1px solid rgba(103, 80, 164, 0.10); + background: linear-gradient(180deg, rgba(103, 80, 164, 0.12), rgba(103, 80, 164, 0.06)); +} + +.notification-md-code-lang { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 24px; + padding: 0 10px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.58); + border: 1px solid rgba(103, 80, 164, 0.14); + color: var(--md-sys-color-primary); + font-size: 11px; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.notification-md pre { + overflow-x: auto; + margin: 0; + padding: 1rem 1.05rem 1.05rem; + border: none; + border-radius: 0 0 18px 18px; + background: + radial-gradient(circle at top right, rgba(103, 80, 164, 0.08), transparent 34%), + linear-gradient(180deg, rgba(248, 245, 255, 0.96), rgba(241, 236, 251, 0.96)); + box-shadow: none; + scrollbar-width: thin; + scrollbar-color: rgba(103, 80, 164, 0.28) transparent; +} + +.notification-md pre code { + display: block; + min-width: max-content; + color: var(--md-sys-color-on-surface); + font-family: "Consolas", "SFMono-Regular", monospace; + font-size: 0.92rem; + line-height: 1.72; + background: transparent; +} + +.notification-md pre code .token { + font-weight: 600; +} + +.notification-md pre code .token-key, +.notification-md pre code .token-property { + color: var(--md-code-token-key); +} + +.notification-md pre code .token-string { + color: var(--md-code-token-string); +} + +.notification-md pre code .token-number { + color: var(--md-code-token-number); +} + +.notification-md pre code .token-boolean { + color: var(--md-code-token-boolean); +} + +.notification-md pre code .token-keyword { + color: var(--md-code-token-keyword); +} + +.notification-md pre code .token-function { + color: var(--md-code-token-function); +} + +.notification-md pre code .token-command { + color: var(--md-code-token-command); + font-weight: 800; +} + +.notification-md pre code .token-flag { + color: var(--md-code-token-flag); +} + +.notification-md pre code .token-punctuation { + color: var(--md-code-token-punctuation); +} + +.notification-md .notification-md-table-wrap { + overflow-x: auto; + border-radius: 16px; + border: 1px solid rgba(103, 80, 164, 0.12); + background: rgba(255, 255, 255, 0.34); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.32); +} + +.notification-md .notification-md-details { + overflow: hidden; + border-radius: 16px; + border: 1px solid rgba(103, 80, 164, 0.16); + background: linear-gradient(180deg, rgba(103, 80, 164, 0.08), rgba(103, 80, 164, 0.03)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3); +} + +.notification-md .notification-md-summary { + position: relative; + display: flex; + align-items: center; + gap: 0.65rem; + padding: 0.88rem 1rem; + cursor: pointer; + list-style: none; + user-select: none; + font-weight: 800; + color: var(--md-sys-color-on-surface); +} + +.notification-md .notification-md-summary::-webkit-details-marker { + display: none; +} + +.notification-md .notification-md-summary::before { + content: '▸'; + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--md-sys-color-primary); + font-size: 0.92rem; + line-height: 1; + transform: rotate(0deg); + transition: transform var(--interactive-transition-duration) var(--theme-transition-easing); +} + +.notification-md .notification-md-details[open] > .notification-md-summary::before { + transform: rotate(90deg); +} + +.notification-md .notification-md-details > :not(summary) { + margin-left: 1rem; + margin-right: 1rem; +} + +.notification-md .notification-md-details > :not(summary):last-child { + margin-bottom: 1rem; +} + +.notification-md .notification-md-details > .notification-md-summary + * { + margin-top: 0.1rem; +} + +.notification-md-mermaid { + overflow: hidden; + padding: 1rem; + background: + radial-gradient(circle at top right, rgba(103, 80, 164, 0.08), transparent 34%), + linear-gradient(180deg, rgba(248, 245, 255, 0.96), rgba(241, 236, 251, 0.96)); + text-align: center; +} + +.notification-md-mermaid-toolbar { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.5rem; + padding: 0.7rem 0.9rem 0; +} + +.notification-md-mermaid-tool-btn { + min-width: 2rem; + height: 2rem; + padding: 0 0.65rem; + border: 1px solid rgba(103, 80, 164, 0.16); + border-radius: 999px; + background: rgba(255, 255, 255, 0.7); + color: var(--md-sys-color-primary); + font-size: 0.9rem; + font-weight: 800; + line-height: 1; + cursor: pointer; + transition: + background-color var(--interactive-transition-duration) var(--theme-transition-easing), + border-color var(--interactive-transition-duration) var(--theme-transition-easing), + transform var(--interactive-transition-duration) var(--theme-transition-easing), + box-shadow var(--interactive-transition-duration) var(--theme-transition-easing); +} + +.notification-md-mermaid-tool-btn:hover { + background: rgba(103, 80, 164, 0.12); + border-color: rgba(103, 80, 164, 0.24); + transform: translateY(-1px); +} + +.notification-md-mermaid-viewport { + position: relative; + overflow: hidden; + width: 100%; + min-height: 240px; + border-radius: 14px; + cursor: default; + touch-action: none; +} + +.notification-md-mermaid-viewport.is-pannable { + cursor: grab; +} + +.notification-md-mermaid-viewport.is-dragging { + cursor: grabbing; +} + +.notification-md-mermaid-canvas { + display: flex; + align-items: center; + justify-content: center; + min-width: 100%; + min-height: 240px; + transform-origin: center center; + will-change: transform; +} + +.notification-md-mermaid svg { + display: block; + max-width: 100%; + height: auto; + margin: 0 auto; + user-select: none; + pointer-events: none; +} + +.notification-md-mermaid.is-error { + text-align: left; + color: #B3261E; + white-space: pre-wrap; + font-family: "Consolas", "SFMono-Regular", monospace; + font-size: 0.9rem; + line-height: 1.7; +} + +.notification-md table { + width: 100%; + min-width: 420px; + border-collapse: collapse; +} + +.notification-md th, +.notification-md td { + padding: 0.72rem 0.85rem; + text-align: left; + vertical-align: top; + border-bottom: 1px solid rgba(103, 80, 164, 0.10); +} + +.notification-md th { + background: rgba(103, 80, 164, 0.10); + color: var(--md-sys-color-primary); + font-weight: 800; + white-space: nowrap; +} + +.notification-md td { + color: var(--md-sys-color-on-surface); +} + +.notification-md tr:last-child td { + border-bottom: none; +} + +.notification-feed-actions { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + padding-top: 0; +} + +.notification-feed-actions .MuiButton-root { + min-height: 40px; + padding: 0 16px; + border-radius: 18px; + border-width: 1px; + font-weight: 700; + background: rgba(255, 255, 255, 0.62); + box-shadow: none; +} + +.notification-feed-actions .MuiButton-root.Mui-disabled { + background: rgba(255, 255, 255, 0.34); + border-color: rgba(103, 80, 164, 0.10); +} + +.notification-feed-actions-inside { + position: absolute; + right: 14px; + bottom: 12px; + justify-content: flex-end; +} + +.task-next-run-primary-row { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: flex-start; +} + +.task-next-run-label { + display: block !important; + margin-bottom: 4px !important; + opacity: 0.68; +} + +.task-next-run-time { + font-weight: 800 !important; + color: var(--md-sys-color-primary); +} + +.task-countdown-text { + opacity: 0.82; +} + +.task-status-pill { + /* 用统一的状态 pill 表达 future / soon / urgent / expired 等时序语义。 */ + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; + white-space: nowrap; +} + +.task-status-pill.is-future { + background: rgba(103, 80, 164, 0.14); + color: var(--md-sys-color-primary); +} + +.task-status-pill.is-soon { + background: rgba(59, 130, 246, 0.12); + color: #2563EB; +} + +.task-status-pill.is-urgent { + background: rgba(245, 158, 11, 0.16); + color: #B45309; +} + +.task-status-pill.is-expired, +.task-status-pill.is-unknown { + background: rgba(244, 67, 54, 0.14); + color: #C62828; +} + +.task-progress-track { + /* 任务与计时器共用同一套进度条视觉语言,降低界面认知成本。 */ + width: 100%; + height: 8px; + border-radius: 999px; + background: rgba(103, 80, 164, 0.12); + overflow: hidden; +} + +.task-progress-bar { + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, #6750A4, #8B5CF6); + transition: width var(--theme-transition-duration) var(--theme-transition-easing); +} + +.task-progress-bar.is-soon { + background: linear-gradient(90deg, #3B82F6, #60A5FA); +} + +.task-progress-bar.is-urgent { + background: linear-gradient(90deg, #F59E0B, #F97316); +} + +.task-progress-bar.is-expired, +.task-progress-bar.is-unknown { + background: linear-gradient(90deg, #EF4444, #F87171); +} + + +body.dark-theme .sidebar-notification-badge { + border-color: rgba(28, 27, 31, 0.42); + box-shadow: 0 10px 22px rgba(255, 107, 107, 0.18); +} + +body.dark-theme .sidebar-github-author { + color: var(--md-sys-color-on-surface); +} + +body.dark-theme .sidebar-github-meta { + color: var(--md-sys-color-on-surface-variant); + opacity: 0.9; +} + +body.dark-theme .notifications-hero-card { + background: + linear-gradient(135deg, rgba(208, 188, 255, 0.10), rgba(79, 55, 139, 0.10) 50%, rgba(28, 27, 31, 0.78) 100%), + var(--md-sys-color-surface); +} + + +body.dark-theme .notification-feed-card { + background: + linear-gradient(180deg, rgba(208, 188, 255, 0.09), rgba(208, 188, 255, 0.025) 34%, rgba(28, 27, 31, 0.88) 100%), + var(--md-sys-color-surface); + border-color: rgba(208, 188, 255, 0.10); +} + +body.dark-theme .markdown-docs-file-item { + background: rgba(255, 255, 255, 0.05); + border-color: rgba(208, 188, 255, 0.12); +} + +body.dark-theme .markdown-docs-file-item:hover { + background: rgba(208, 188, 255, 0.12); + border-color: rgba(208, 188, 255, 0.20); + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22); +} + +body.dark-theme .markdown-docs-file-item.is-active { + background: rgba(208, 188, 255, 0.18); + color: var(--md-sys-color-on-surface); + border-color: rgba(208, 188, 255, 0.24); +} + +body.dark-theme .notifications-actions-row .MuiButton-root { + box-shadow: none; +} + +body.dark-theme .notifications-inline-meta-item.is-pill { + background: rgba(255, 255, 255, 0.04); + border-color: rgba(208, 188, 255, 0.12); + box-shadow: none; +} + +body.dark-theme .notification-feed-status-pill.is-read { + color: #C7CFD9; + background: rgba(199, 207, 217, 0.12); + border-color: rgba(199, 207, 217, 0.18); +} + +body.dark-theme .notification-feed-status-pill.is-unread { + color: #FBC02D; + background: rgba(251, 192, 45, 0.14); + border-color: rgba(251, 192, 45, 0.22); +} + +body.dark-theme .notification-feed-content-panel { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.03)); + border-color: rgba(208, 188, 255, 0.10); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +body.dark-theme .notification-md h1 { + border-bottom-color: rgba(208, 188, 255, 0.18); +} + +body.dark-theme .notification-md h3, +body.dark-theme .notification-md a, +body.dark-theme .notification-md :not(pre) > code { + color: var(--md-sys-color-primary); +} + +body.dark-theme .notification-md a { + border-bottom-color: rgba(208, 188, 255, 0.26); +} + +body.dark-theme .notification-md a:hover { + border-bottom-color: rgba(208, 188, 255, 0.42); +} + +body.dark-theme .notification-md hr { + background: linear-gradient( + 90deg, + transparent, + rgba(208, 188, 255, 0.26), + transparent + ); +} + +body.dark-theme .notification-md blockquote { + background: linear-gradient(135deg, rgba(208, 188, 255, 0.14), rgba(79, 55, 139, 0.18)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +body.dark-theme .notification-md .notification-md-callout { + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +body.dark-theme .notification-md .notification-md-callout.is-note { + background: linear-gradient(135deg, rgba(96, 165, 250, 0.16), rgba(30, 64, 175, 0.18)); +} + +body.dark-theme .notification-md .notification-md-callout.is-note .notification-md-callout-header { + color: #93C5FD; +} + +body.dark-theme .notification-md .notification-md-callout.is-tip { + background: linear-gradient(135deg, rgba(74, 222, 128, 0.16), rgba(21, 128, 61, 0.18)); +} + +body.dark-theme .notification-md .notification-md-callout.is-tip .notification-md-callout-header { + color: #86EFAC; +} + +body.dark-theme .notification-md .notification-md-callout.is-important { + background: linear-gradient(135deg, rgba(196, 181, 253, 0.18), rgba(91, 33, 182, 0.20)); +} + +body.dark-theme .notification-md .notification-md-callout.is-important .notification-md-callout-header { + color: #D8B4FE; +} + +body.dark-theme .notification-md .notification-md-callout.is-warning { + background: linear-gradient(135deg, rgba(251, 191, 36, 0.18), rgba(180, 83, 9, 0.18)); +} + +body.dark-theme .notification-md .notification-md-callout.is-warning .notification-md-callout-header { + color: #FCD34D; +} + +body.dark-theme .notification-md .notification-md-callout.is-caution { + background: linear-gradient(135deg, rgba(248, 113, 113, 0.18), rgba(153, 27, 27, 0.18)); +} + +body.dark-theme .notification-md .notification-md-callout.is-caution .notification-md-callout-header { + color: #FCA5A5; +} + +body.dark-theme .notification-md :not(pre) > code { + background: rgba(208, 188, 255, 0.12); + border-color: rgba(208, 188, 255, 0.18); +} + +body.dark-theme .notification-md-code-block, +body.dark-theme .notification-md-mermaid-block { + border-color: rgba(208, 188, 255, 0.18); + background: + linear-gradient(180deg, rgba(208, 188, 255, 0.12), rgba(79, 55, 139, 0.08) 16%, rgba(23, 20, 29, 0.96) 100%), + rgba(23, 20, 29, 0.96); + box-shadow: + 0 12px 28px rgba(0, 0, 0, 0.28), + inset 0 1px 0 rgba(255, 255, 255, 0.06); +} + +body.dark-theme .notification-md-code-header { + border-bottom-color: rgba(208, 188, 255, 0.12); + background: linear-gradient(180deg, rgba(208, 188, 255, 0.14), rgba(79, 55, 139, 0.20)); +} + +body.dark-theme .notification-md-code-lang { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(208, 188, 255, 0.18); +} + +body.dark-theme .notification-md pre { + background: + radial-gradient(circle at top right, rgba(208, 188, 255, 0.08), transparent 34%), + linear-gradient(180deg, rgba(30, 27, 38, 0.98), rgba(20, 18, 24, 0.98)); +} + +body.dark-theme .notification-md pre code { + color: #ECE7F6; +} + +body.dark-theme .notification-md pre code .token-key, +body.dark-theme .notification-md pre code .token-property { + color: var(--md-code-token-key); +} + +body.dark-theme .notification-md pre code .token-string { + color: var(--md-code-token-string); +} + +body.dark-theme .notification-md pre code .token-number { + color: var(--md-code-token-number); +} + +body.dark-theme .notification-md pre code .token-boolean { + color: var(--md-code-token-boolean); +} + +body.dark-theme .notification-md pre code .token-keyword { + color: var(--md-code-token-keyword); +} + +body.dark-theme .notification-md pre code .token-function { + color: var(--md-code-token-function); +} + +body.dark-theme .notification-md pre code .token-command { + color: var(--md-code-token-command); +} + +body.dark-theme .notification-md pre code .token-flag { + color: var(--md-code-token-flag); +} + +body.dark-theme .notification-md pre code .token-punctuation { + color: var(--md-code-token-punctuation); +} + +body.dark-theme .notification-md .notification-md-table-wrap { + background: rgba(255, 255, 255, 0.04); + border-color: rgba(208, 188, 255, 0.14); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); +} + +body.dark-theme .notification-md .notification-md-details { + background: linear-gradient(180deg, rgba(208, 188, 255, 0.12), rgba(79, 55, 139, 0.10)); + border-color: rgba(208, 188, 255, 0.18); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +body.dark-theme .notification-md .notification-md-summary { + color: var(--md-sys-color-on-surface); +} + +body.dark-theme .notification-md-mermaid { + background: + radial-gradient(circle at top right, rgba(208, 188, 255, 0.08), transparent 34%), + linear-gradient(180deg, rgba(30, 27, 38, 0.98), rgba(20, 18, 24, 0.98)); +} + +body.dark-theme .notification-md-mermaid-tool-btn { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(208, 188, 255, 0.18); + color: var(--md-sys-color-primary); +} + +body.dark-theme .notification-md-mermaid-tool-btn:hover { + background: rgba(208, 188, 255, 0.14); + border-color: rgba(208, 188, 255, 0.24); +} + +body.dark-theme .notification-md-mermaid.is-error { + color: #F2B8B5; +} + +body.dark-theme .notification-md th, +body.dark-theme .notification-md td { + border-bottom-color: rgba(208, 188, 255, 0.10); +} + +body.dark-theme .notification-md th { + background: rgba(208, 188, 255, 0.10); +} + +body.dark-theme .notification-feed-actions .MuiButton-root { + background: rgba(255, 255, 255, 0.06); + border-color: rgba(208, 188, 255, 0.18); + box-shadow: none; +} + +body.dark-theme .notification-feed-actions .MuiButton-root.Mui-disabled { + background: rgba(255, 255, 255, 0.04); + border-color: rgba(208, 188, 255, 0.10); +} + +body.dark-theme .status-metric-label, +body.dark-theme .status-timer-section-desc, +body.dark-theme .status-timer-kicker, +body.dark-theme .status-timer-session-sub, +body.dark-theme .task-card-kicker, +body.dark-theme .task-card-session-sub, +body.dark-theme .task-next-run-label, +body.dark-theme .status-timer-label, +body.dark-theme .status-timer-info-label, +body.dark-theme .tasks-header-subtitle, +body.dark-theme .task-countdown-text, +body.dark-theme .notifications-inline-meta-label, +body.dark-theme .notification-feed-time { + color: var(--md-sys-color-on-surface-variant); + opacity: 0.95; +} + +body.dark-theme .status-timer-section-count, +body.dark-theme .status-timers-summary-pill, +body.dark-theme .header-clock-chip, +body.dark-theme .connection-chip { + color: var(--md-sys-color-on-surface); +} + +/* ========== 响应式设计 ========== */ + +/* 平板设备:缩窄侧边栏、把 12 栏压缩为 6 栏,兼顾内容密度与可读性。 */ +@media (max-width: 1024px) { + .sidebar { + width: 240px; + } + + .top-bar { + padding: 0 24px; + } + + .main-content { + padding: calc(var(--top-bar-height) + 16px) 24px 24px; + } + + .dashboard-grid { + gap: 16px; + grid-template-columns: repeat(6, 1fr); + } + + .span-4 { grid-column: span 3; } + .span-8 { grid-column: span 6; } + .span-12 { grid-column: span 6; } + + .card { + padding: 20px; + } +} + +/* 移动设备:整体从左右分栏切换为上下堆叠,导航改为横向可滚动。 */ +@media (max-width: 768px) { + .app { + flex-direction: column; + height: auto; + min-height: 100vh; + } + + .sidebar { + width: 100%; + height: auto; + padding: 12px 16px; + border-right: none; + border-bottom: 1px solid var(--glass-border); + flex-direction: column; + gap: 12px; + } + + .sidebar-header { + padding: 0; + flex-direction: row; + justify-content: space-between; + width: 100%; + } + + .sidebar-logo-img { + width: 40px; + height: 40px; + } + + .sidebar > div:nth-child(2) { + display: flex; + flex-direction: row; + gap: 8px; + overflow-x: auto; + overflow-y: hidden; + margin: 0 !important; + padding-bottom: 4px; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + } + + .nav-item { + white-space: nowrap; + padding: 10px 16px; + margin-bottom: 0; + flex-shrink: 0; + font-size: 14px; + } + + .sidebar > div:nth-child(3) { + display: none; + } + + .top-bar { + position: relative; + top: auto; + left: auto; + right: auto; + height: auto; + min-height: calc(var(--top-bar-height) * 0.75); + padding: 12px 16px; + flex-wrap: wrap; + gap: 12px; + } + + .top-bar::before { + display: none; + } + + .top-bar > h5 { + font-size: 1.25rem; + } + + .main-wrapper { + flex: 1; + overflow: visible; + } + + .main-content { + padding: 16px; + overflow-y: visible; + height: auto; + } + + .dashboard-grid { + grid-template-columns: 1fr !important; + gap: 12px; + } + + .span-4, .span-8, .span-12 { + grid-column: span 1 !important; + } + + .card { + padding: 16px; + border-radius: var(--radius-md); + } + + .card:hover { + transform: none; + } + + .top-bar > div { + width: 100%; + justify-content: space-between; + } + + .tasks-header-row, + .status-timer-section-head { + flex-direction: column; + align-items: flex-start; + } + + .notifications-actions-row { + width: 100%; + justify-content: flex-start; + padding-left: 0; + } + + .notifications-title-row { + align-items: flex-start; + } + + .notifications-inline-meta { + width: 100%; + gap: 8px 10px; + } + + .status-timer-info-grid { + grid-template-columns: 1fr; + } + + .status-panel-card { + min-height: auto; + } + + .tasks-grid-enhanced { + grid-template-columns: 1fr; + } + + .markdown-docs-shell { + grid-template-columns: 1fr; + } + + .markdown-docs-aside-sticky { + position: static; + } + + .markdown-docs-sidebar-card { + min-height: auto; + height: auto; + max-height: none; + overflow: visible; + } + + .markdown-docs-content-card { + min-height: auto; + } + + .markdown-docs-file-list { + min-height: auto; + flex: initial; + overflow: visible; + padding-right: 0; + } +} + +/* 小屏手机:进一步压缩边距与卡片留白,保证信息仍能在窄屏完整呈现。 */ +@media (max-width: 480px) { + .notification-md { + font-size: 0.94rem; + } + + .notification-md h1 { + font-size: 1.18rem; + } + + .notification-md h2 { + font-size: 1.08rem; + } + + .notification-md h3, + .notification-md h4, + .notification-md h5, + .notification-md h6 { + font-size: 0.98rem; + } + + .notification-md-code-header { + min-height: 38px; + padding: 0 12px; + } + + .notification-md-code-lang { + min-height: 22px; + padding: 0 9px; + font-size: 10px; + } + + .notification-md pre { + padding: 0.85rem 0.9rem 0.95rem; + border-radius: 0 0 14px 14px; + } + + .notification-md .notification-md-table-wrap { + margin-left: -2px; + margin-right: -2px; + } + + .notification-md .notification-md-summary { + padding: 0.78rem 0.88rem; + } + + .notification-md .notification-md-callout { + padding: 0.9rem 0.92rem 0.9rem 0.98rem; + } + + .notification-md .notification-md-callout-header { + margin-bottom: 0.6rem; + } + + .notification-md-mermaid { + padding: 0.85rem; + } + .sidebar { + padding: 10px 12px; + } + + .sidebar-header { + gap: 8px; + } + + .sidebar-logo-img { + width: 36px; + height: 36px; + } + + .nav-item { + padding: 8px 14px; + font-size: 13px; + } + + .top-bar { + padding: 10px 12px; + min-height: 56px; + } + + .main-content { + padding: 12px; + } + + .card { + padding: 14px; + } + + .header-clock-chip, + .connection-chip { + width: 100%; + justify-content: center; + } + + .task-next-run-primary-row, + .task-card-top, + .status-timer-primary-row { + flex-direction: column; + align-items: flex-start; + } + + .notification-feed-side, + .notification-feed-actions { + width: 100%; + justify-content: flex-start; + } + + .notification-feed-card-top { + align-items: flex-start; + } + + .notification-feed-heading-group { + width: 100%; + flex-wrap: wrap; + gap: 8px; + } + + .notification-feed-title { + width: 100%; + white-space: normal; + } + + .notification-feed-meta-inline { + padding-left: 0; + gap: 8px; + } + + .notification-feed-content-panel { + padding: 14px 15px 54px; + } + + .notification-feed-actions-inside { + right: 12px; + bottom: 10px; + } + + .status-timer-card-top { + grid-template-columns: 1fr; + } + + .status-timer-chip-stack { + justify-content: flex-start; + max-width: none; + } + + .status-action-tooltip-bubble { + left: 0; + transform: translateX(0) translateY(6px); + width: 100%; + max-width: none; + text-align: left; + } + + .status-action-tooltip-bubble::after { + left: 22px; + transform: none; + } + + .status-action-tooltip-wrap:hover .status-action-tooltip-bubble, + .status-action-tooltip-wrap:focus-within .status-action-tooltip-bubble { + transform: translateX(0) translateY(0); + } +} + +/* 超小屏幕:针对极窄设备继续缩小导航与容器 padding。 */ +@media (max-width: 360px) { + .sidebar { + padding: 8px 10px; + } + + .sidebar-logo-img { + width: 32px; + height: 32px; + } + + .nav-item { + padding: 7px 12px; + font-size: 12px; + } + + .top-bar { + padding: 8px 10px; + } + + .main-content { + padding: 10px; + } + + .card { + padding: 12px; + } +} + +/* ===== 动画、骨架屏与低动态偏好兼容 ===== */ +/* ========== 骨架屏动画 ========== */ +@keyframes card-fade-up { + 0% { + opacity: 0; + transform: translateY(10px) scale(0.985); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.skeleton { + /* skeleton 类可为未来异步区域提供统一的闪烁占位样式。 */ + background: linear-gradient( + 90deg, + var(--md-sys-color-surface-variant) 0%, + rgba(var(--md-sys-color-on-surface-variant-rgb, 73, 69, 79), 0.15) 50%, + var(--md-sys-color-surface-variant) 100% + ); + background-size: 200% 100%; + animation: skeleton-loading 1.5s ease-in-out infinite; + border-radius: var(--radius-sm); + will-change: background-position; +} + +@keyframes skeleton-loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +/* 群沉默重置提示中的小圆点脉冲动画。 */ +@keyframes status-reset-dot-pulse { + 0% { + transform: scale(0.9); + box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.34); + } + 70% { + transform: scale(1); + box-shadow: 0 0 0 8px rgba(37, 99, 235, 0); + } + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); + } +} + +/* 群沉默重置提示条的淡入停留淡出动画。 */ +@keyframes status-reset-hint-fade { + 0% { + opacity: 0; + transform: translateY(-6px) scale(0.98); + } + 10% { + opacity: 1; + transform: translateY(0) scale(1); + } + 82% { + opacity: 1; + transform: translateY(0) scale(1); + } + 100% { + opacity: 0; + transform: translateY(-4px) scale(0.985); + } +} + +body.dark-theme .skeleton, +html.theme-dark .skeleton { + background: linear-gradient( + 90deg, + var(--md-sys-color-surface-variant) 0%, + rgba(202, 196, 208, 0.15) 50%, + var(--md-sys-color-surface-variant) 100% + ); + background-size: 200% 100%; +} + +/* 尊重系统“减少动态效果”偏好,关闭高频动画,提升无障碍体验。 */ +@media (prefers-reduced-motion: reduce) { + .boot-loader__logo, + .boot-loader__progress-bar, + .skeleton, + .status-timer-reset-dot, + .status-timer-reset-hint, + .status-panel-card, + .tasks-summary-card, + .task-card-enhanced, + .status-timer-card, + .status-timer-section-block, + .tasks-empty-card, + .status-timers-empty-card, + .notifications-hero-card, + .notifications-empty-card, + .markdown-docs-sidebar-card, + .markdown-docs-content-card, + .markdown-docs-empty-card, + .markdown-docs-empty-side-card, + .config-card, + .config-header, + .config-renderer-shell, + .config-renderer-main, + .config-renderer-aside, + .config-renderer-form-card, + .config-renderer-preview-card, + .config-renderer-session-card, + .notifications-inline-meta-item, + .markdown-docs-file-item { + animation: none !important; + } + + #loading-skeleton.is-exiting { + transition: opacity 0.15s linear !important; + transform: none !important; + } + + .boot-loader__progress-bar { + transform: translate3d(0, 0, 0) scaleX(1); + } +} diff --git a/pages/proactive-chat/fonts/outfit-400.ttf b/pages/proactive-chat/fonts/outfit-400.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0b0a0d7d6c94b22d97c44352fbf0657e2a26ac1f GIT binary patch literal 47844 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 z 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 literal 0 HcmV?d00001 diff --git a/pages/proactive-chat/fonts/outfit-500.ttf b/pages/proactive-chat/fonts/outfit-500.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0ba786a6ecc7a7b9d168248dd49c35dfd8f8a08f GIT binary patch literal 47800 zcmd4434B$>*}y$>ZgR7SkdOpI$i8o6Aq$WM2!Vth5fCUlVF^pX5LN{NK@o97v{qZ2 zQfsYRtGHoQOqE)Rh)4x3jSwlth&GLB8gb#??>}>Hk{bw0-}n7}--%R`K38!kX%CUh-0LR~o4nz{rCa%XE`KL2STS$aaMv}yLU@%?z}Us} zR<9!7kN11=>b`jShDA63dSDv2dJ8e*=_L#2l_j>!P-XAt{hTF4^nBK*gunCoJ7&p> zs`aUl4}4vS9?w#T#^oy)%zJOeOO)@vocgq_n74kF+#@Zd4<&s>#k>^@)9#(VLI}&# zykER(?Y&i%3s)_%?%qLqZ_>LRkoFzVy4=N|LfY-32d_eI6fX#0 zF-}<3Urs+3m5Q7jZQl#wyWPIoHrBV~vNY;$mC9Ym_tIggFxD+zQYBSxhK>=^DqD5V zDo*x!Sf;)kI;Thee}u)-sN(DY_79tSZ5R}sL^*qWODzB7Pb>GEKQ6`)a&RTXUB)z` z#+a{iw#bwbW|EGuODh&t&RZ;Mh0k|}*$zvibRnfqBO8p@xQ-AW{B4M1T+@?smFPu# zYQ%o=B)_}FG!ZAlbW6?)RV#@mNjaW0X(EaDqIfq%1POnHJWEK=<3f6j6wjz(=Xi~Eyw0GuAzXWbuLviM6fuPHg8GOIkxMvGsJ zatPr}#*9VIm&=4hvyg{D8Wk^yU!wY#c0Q=yck$r*32Aj)OWO0LaTS9^l_TCt{0zsnugF0XX8ua1y&cy*x%M`%(OWfEbQ;Q6@i&NtqFgLSK2?m9 zg-EJgtPv~31i~t@h`*}zdsSkUSWU_O`CTm*h)UjB#e1vKl+|1>=XE8qi+O*77>V?q zb4cxy_f*QM)OW2E73gRN_h!)AbTOFw7m@2qewUNtC&Z=EpJ^hKo}JYtd3A14gqTR`mFUz$q*2AY z!?eVf5U1J}Arh{T+hT__*PxSW+_e%pD`|B~x}3PKF{#K>Nl8gM5#1BQQw$aNVh~4; zoh+i3&#S6HXVkbCSUp!#81%0TSDkUnR+(|u1q)WJ5~VsU#C|LgdCSWe&l8!eD%PwJ zDODq`NqRt^GOU*#+G#+NPsVyM zo{445FqSOXl^{}FzycoO?>&sZ!|1<<(_e=foo|RQu87+~E}`7*hYUU^x5ve2g!@Dz z;m;VA2DwQ}*n@pL%#~_~s*9EM%33#zZ**KoyuD4wf2(F4@@|h$AvTD6sI_!sT!)HQ zVtj>Fy6V4L)Y#RrO4?tEe-pOft4ivv>?N;_x|VyWr>fb{oKmS;`GUmq7~)~oxf_gc6k$dMk)YSA`HYMUhGQc6~- zEtK{%?)jCj&pt|4X;s<7sea)W$D{z-ly zzcW&eGUEZ`MdOb~lku&^YVo&(S&}R{mWh^~mc5p0%hQ&(EuUHbZLzy}xdgjJyYzP% z>@vY+hRX(*y)M-*`&|yY)VZ8=X?E@5n&vvlb&+eO>t@$ou5Y>?bv@;J-nGS=XDznw zu-<1q>*np2?Y7qKH*U|l{nPCm_Z;^k_ucLfxj*Lqjfb~KibtNuOphfVRUTVBc6&VJ z@vg^59)I`vm#2$ojAy!MzGtcDWX}zr4|?wNe8#iJ^KH+6cz)&8&nwZZ%xjg`O^P?}*>W ze&_uD)zhVCQO_kkt9t&n=kq-e_WaI2)qkLWvHt}BpZGuG|G0mH{}=vW2V@6S1w0#Y zEZ}dw+(@7`@1(x>_5FR{e}*Q67KHvZ^n-queqsH__q(g#@A`cn785ooY;V}nuz!U24_^}g zr|^#>ED?hvrZ|3=Mcf(jT*RjlUq$*xrbI4|{8i+Wk#9$yi*kz!ii(ZOh$@I06*VQQ zGU}eFx@gzve$m;{1<@m;w?x-OpNjrACLksyCO>9u%>0;DF}KCE#umg@#2$-Fi`yFa zRNO~#=i)BLkB;9G|9*mh!lZ;v2~Q=|BpglnTf)B+!xJ+TM1H#I0VEHy4QEj1@~s@8io5|oALMX1IaHqIrF*Ck>ob~Zuy zV6z9}TRbh@WPt1|qh-8ImRT}a7RpjNPX0vBlMCf$dDQ4@Og`xLDjR-i(Zd zupMEKhW$IDS42oeSVUArT*RP=;)tq%Q8mwdq!_v(%Z(z7QqPgq@=j8EtJ#}5fITkB2q`YTURxMn^k8Qc_b=yg}SbWQL{D3TxM=uY-0*=AEnTTgOojF|jOH0K{`k^1aew~bz70lGP zh%I6Vv)CRy%%5sY{ur~sQ&`OX;x%!Q+3hW6s3YPeeq}vgmiY9t^A~trd%$MV(2hv(8C!t-v|2PxRYMB@<-oXQ^66?e!W}BPQ=Uc@+ ztPysLyTqg75$5(^h}*=o;wAAsGkuMCQH~a`h!4bj;(gW^r^V}{f!TH=V`L38_f4$8 zwlYs`XSUuZ0>$mjPCHpm+{+ApH-7tlqK~+f^~GL%)(4qc9>DK-2(PG`vGgmEA|7U3 z{SyD{H)4SJEuQV~M7DSWAMi=E|Mwza`~ko38Br*n6C?20i^a>#jj!U<)#6XRDaMOG zitEH7alLq3OcH;>%dHc0#b59zkK%is5Oc(FJi-srzQ5udp1}`WCH{`z_78ERIEN>& z7Qf}std8E5tC`uVVcuLM7c<6YF^?|9`&ob$_!~ap$M~72@Y6n(DKb?i zFe;N}qMRWo%c*i27GR3JK~6WGV|8~>^uAUsCrMi&#u`tF#49*Q{6*eZuU|6?#`F4P zpU@**S^xr{vp(JJp#NO*CDb=1RK?)Q^Z}ya#3IzP279k zV+j!nKZeUDkzmZ^ey|Ln%~f1GlOjTl%Q;G#WyqnO{i4{o77mEs$T#G2j2iN)7C~1q zPDB~b6pL`<8nHa2(-z3jMA+r%JTgU;#URZz2ohmGhP+>R8$(FvOuQ&C;<0%b7%`2E z^F~Htqy0^&gKuQLisxE+-Yxdc3TYPwiuUyKaeW;l_t2NKbbcz&E6JkltK*~Iv;2xQ z#oTKWE`LW^Cb7K6$Y(^r(iniJ6R!evIv!vL~r(gLeU3-Ro2KT zQ7W;7aFz0nL{;u(0}i7{N)!#fawoP&6EVJ1@^BR}?^GlO;WgKi7vy~EOw7Ur4x;`h>p zwdDe4w0rS4;>0C9urhi#UVP5H?oRC&N)I%lPI@vYd7&q2KEH@x<4aHV5Gm3RZ{j^> zw{OM!%9~-vPoqZt@p}TP)mi1i$Y4B%K4KBQk;#nx0W;QL@QFg1i~qp9p2Z4%0P^jJ zPZW+`MKBY7hd&b~a`Bd8@P0l-1}B*BPU7vvGsh+34-L{Y9djP@alZIMrioW&e>5mv zoR=BQ%jNi8S$G%A#Zox{->QPSwwgJ6q_E-stwg#d;sU;mWd2?y^O%EI#XniYuG>&KOpKF7V!RwKCg3lPK#%{9S9TpzU5jaRoqR^Y|0!W(QA{}FBSMp=okQ6<+1i(HH6)&t)y43E); zeaR@+-*4eT`Cz&I@j(6XBA*vU_<7#=Z*%d2hT`k|39sx$yt9|_m7df-rmOIj>+r2S z@Vsur+fu&Vt@u8Tay@?8>%vWLz)O7)ZS)oYVt3?#_IJbaS?7tzup&1JcX_ki#LDFs zxkYZ3+vKfsyWAmflef#A@(#I6-YIv>yX4*S9(k|4kKOzG@$Krxa6HB{ctoG#8GV9> zbqepWLGHB04gyYr`=B;LynVFs?9Z6hD=g+G&MvZnG}5w z$tityj`eySt252a$WyNwBLn%JPcE)*Ez& zUNl#z`Yr15(#!=lh4%x5KvR|h&@>6*BObIPuWskBthsdC%6uySQu)gmhcMZHw5 zQ-8TvEva0n{&rima!sXvEnlnOUtPXly}z2_R-rBymh;p~C8#LZ8ECPj7Y%g0W;$N8 z)N5vX?f_%>Br|GQx{k`tFyG3^Om`WvrgEi@%*@C(6BT8<&8w_jxo*uW9iK5U*Hiyp zsY-Q3<$36+vX$#995DmUnB@x>wWrC^*=42c5tE*kp6j7SxnSjr`5s!D>dKL($V-`# ztn}e&^Oo!Txi*_YoFh$pe#1Pf%9ocdbh>+hnIgk+N2ViXrm31)nR%Y`)bqy5m8+I4 zRP#4e75l*n%rEA?yYFTD?rL{P@2)OeSXm*WR?MqhCPG#$Td|B;K)s{hS9`L`FHm{8 z`1l$1*l15II#0%URq+q=E16%_bD61>HJbUeka;(c`8ox!AcA>BiuFb_;W^_W!X~^% zDIT|22~V+#Q{nf7C-l4b8>hMcwNC#CekM<@NYB2E_hwiRm9QKZ!CWWsn7nzC>^&Yz zsdd*US+2Ua>|uKkA~%cIGrC@`?8aXZUx+X9BV6T4{Ln+x+nc*pz16N6PauVq|1m?^ zKz)9zbNVgU9?VU*G4F0s6&=>W3Lh_klWZ)&k_cN9E)?S;Y_% z<2qTzSl(ALgv40hS24yY6+=jjF-pbYbExv)QZeE!VlMB=1L_@l08d^$NmZ%t=h}QO zfp&O?dhley<6d;&ZTDvP3-0IKPwVhw9UgZIkLc^Sby%xIGkuM|Hs5{0{Ta>*JfXvV zIy7T`uCE`YeBpkt4tMEryAC(IuXnF>U#`PNPT^dAJwu0+bvRy!X8KZhde1#yhvqfU z7KllAPjZiO4|DJB?(gocudO;1Ds=l+hpjqn((yVyhZ7oAT~z3H)~%lKqz=t%Gd$*2 zNBkkTgMM3qdsv5ib$GW9cRF2fQ`e4^=DRn$ttIDGI$Wwl zGv=&YnXZotXX|S-#tf&qF+$wN>d?F%={D4DklO$qrs*(2hfz8Vb-E7H*JgS@eeLOn zZdh$Pyr{z#9e%09^VSC5J*C4Bb$C>V@96MN)q1Ns(;%!b>F`+{K52c-`l$6G9q!TL zZl~}zeZ5788+B-=uhQ4%yA{?Yn7 z(?4m&;#v3TaElImsgO8dtGm@uq3icLY|~-04lk(COyhda>H4(m$F9eTr=Di$dc=w? zb$wfh=Cv8ty4Dc?f-57%^$8vB)1evjbEoSEUGL?Y+IAgo*5P^`RytiTce-Ar%bKgh z89Fp$CcBQOtWq5o>ClWZ!+h5q-gOSsU6Wj6T*GwOTZjHS^wy!(>00P(GrbO7zIEZG zjZ2db8+CYAhxICSB@H1t6Otz(c@mN*cb#-OMp&oALpnUD!vik+iGSMVaYQS{1L`z~ z_!avZ%uV|GxBB`kJ$rto=hm>@7P2>Mo1Ai!~b8;&tOS!ZW(`GrIIMI{u7K^O27KNXLJq z%L>tDh3K+EbXg&~tPowwce)g%jgn^|lp=fSG`)11UOG)L{cbOv|6yI$3!L$hqRLXE zLS3KZdUjZ&-(90qR%x%JO8XmCDo;`>Z=_1stxBg+Pl|}q`9~?K%HH-o!a!Z;KwalR z9UrJ`6{u?!sB5)N*E~?)8>nj@tWyT-G{HKJkA62;zZ<3eA{nJ~j?y_t={%!!`Y0{= zD0P?Nrtj)))=J+SZMmI$qb(onkXFlR)oMA>azuwab*SE*sAR%Ef|BP%o#qRj<_n8T z^M%gg3!UZ*o#qRjX1e7p?@qUHCW=tyIbElpuG3GqJf>q*&VSOap04kmuJ6s!X&70& zo1n>f^T{`7mDy8wRD#dtIm$g{GyI8-wSm(1? z=d)PnbECe#(NV6dVX}@%)-i+CwYW`{MM%2{kt89~AVgw>+{F{5dcs@HP~FQ@F@3KY z>fG+qy>OS7#$CFUyUbE_TL$a44AyA|>pBnCbsnr+6s^;%9dRiK>$}QzI8~=vYF_Jb ztgGq3|8=}W|em4b3GHL!bIvm8j5L0A+PTdC)Ex!A!`Q}s*xhj>99$MkL&Q14o^tc z`}gbXU+eG@9iAt2UQsL)uKdk;IoDFO&oCO42kBB}St5F|+jYf{9b0d|sG{xXu6WZC zVgI)M9eb0yvNyE<-T7*7A--At%`*|M?6rjFRL=IJ_9pu&9rv-l+WwmTY5T+WJv#kP zV$HI-yZy)B*nV~8Yx{e4p3reGb-&ghr?l>>PUd~~F7S7){X_dNRBGh+q+B!f@8;@5(cAtwJrT+^QZ;MKUA?(hwKwI5X@cf8Tj2BAtt(}NfltiiDf{=5cb8G5oipUxV22z%gtwH=7n zb|4cwP=TG;j2+0w4(wrP^`|@~AIHA%>pbT^%klO6OP zJQJv7cRp2A={@-MGMoMPjrb#KSKMGnLDj>`uCfn*soJ=(YaPM0vQsWRhlu4mo?nBf z4gCo-umgs!rHkI1H#i9~hLq~8r$N1^F@C30BX?bUZ(VyIPD5Zn#V!2033Vz$vXiaO z-tOVoL)$e;tA5J6kFjsA-{r0&SUOkQeG;qs z3H!`mwElDAFNiPMdr&R+(@#Nq@Dx5-`0_+Pl~6s2=&7Y3Ic-tEu7;9@FLjS3e=l-Z zJ1;I;79P51Ty)R4(&iFsR?4r7Zl{lKr>kzKr*5a0ZlOU7x08ozo3HLGsoNuIPYva$ z_NY^khmp~{{HjxvC+Gv!k^tR@zPbK)HOL{O* zO{RuI{HFh*Zh!anU*3Ow$M3%Wm;I(bmU1+u_KM$U5|6~6>KYsW#O1&J_r=!4)JC3- zNC|IJzbVgL`P=W=zWaKw>K)bo8&nlEp7UvGK`|Y_L;1VQZ(v(sbKr%*bG;Uk?($!C zZ(6TuoRs^y|1N%4_1x%J<=f`lJFrdt?PK$~=+orW;PaBt6FztQO!mp~arfTuy`A5w zp6|G)Sy#D!>)Pu2sOw%%7%C>ZP7F#j_N3GryE#ErWz2BhOn)V#9oSMdB9d1%F4Qa@ z&-0l?#zZ(z&a&7W%wtR>>a#(EnL8))gmfzUQONnAm5kWc{7%**c?u)>er8#9;&L%h zcJ?v*KE`i3I`Rg(^cKJCwT^7iI$pIByigY9~&{@%%^Gh@Wb0dW6xD zBYvSr$1gc8H9}PDG4dEXs^-cTteLX!Ut?v}JopW`h*sv3He$Z!-ETk=qeAufANVxJ z6T$!sxPU8I!42HO13bYCyuk-c?`uCGdVnAF1b+x1z83^S5ClVS=mR0p7eb*Qgh4n& zKqU7@K{UibEOGIW2uY9(DUb?j&>u1&6S80cWJ3<*LLLl+K`B0%VNe9a zp%_NMNGO3)7)9FAFox@~ypDtMFaajo&x@({ec}d~2Gi{)#SHif%!FAm8|J`Vm*24za2sgpaun9K97T5~g;8xfU zJK%QM33tFQxD$56U2r$t1NXvxu!mXweqQ&&1Mnd1hv(r1coANLm*D`s0yXd|9E7*v zFnQF$yKscR-vhif@je`b58ycb1wMomaMJ#^_z2DXgxAw>1{&Z~I8Q#GK_h$)7fAmF zd}(izKK4^Gn^)DRULqa8WFl8@**|6!@^Kz$wl|>Ruc7lt#MjK*7t#H1>^5ekbo_^j zyjxCQZ`luuv-pt)v0M17p69{7crZP{4|;+>L_!oqLktXop)d@JU^o=R2p9<^PzqyU zAJ4cY;e0I8wd9?Shw|U$KOZ^PBgb>du^u_rBgcB=SdSd*v22pi01LQ)D_Fq|+`$7p zfzxD+?s{fLUsfW%JoWI!?@?ZdFILr;XWYI#srJS5@I_vA%(%X+iF`S!=*yFCUpyaQ zJU(Ae4)}6*z?b>hm)Y1Cd+X~MF$Wki2N*F27zqa%3CHP$w;2h~FcO|&Bs{}Nc!rVi z3?tzj*4UpFkUwV?{W(+M}`tgb&N2K-qa`D1VWIXU2u#r4N(`(thW^?Al^yxt1i zVF%m}JK+x41$V-3xC`!vd*EKU5B4Da`+40955R-4AD)L7;6-=|UIuI)rz`#O8vOBu z{PBSN83oOZf@VfRGriI%>ai4`@Om1~Km&XVXYF62;mv4xGaBBEhBu3^@R(b8@88Jw zYx_%hg8rOHl}N|{3%GzQSiue4!2>+O3%tQc^&7eL06*vn{t!UC`XpB%sLy8vLvQE< zAp&x`nI7C1s_eMc9#6T=@@sJ2fkPIo13TesA(Sx`hCvYwhhi83BcTLJVH9ac!x*l|@;VO2!vt83A72G)U@feJ z^{@dp!cA~9Y=X_O1-8OAxD~d;4!9k5!X2;+?u6ZN7u*f^z`bxE?7^Pg&+A@z03L*= zuq{u+@8KEv13U}Q!G7L<9$tVK;U#z(4!|o=1Fynsq~}(1Zr&%SP|uXR`a!1MCbO8g z-ALW+NZm~B)NJ0yyN$ft$o$gW@op1&DPOmNR4wGC{M}~qJ4k*9Wf*PBWG1ob-*Ps3%1|_r1-1*lF%- z>bTF~uM3oVPWjsSC7kOe{M!BrDH^%2k^36BuK~Nb8aY(K8dwYKU_ESrjc^m(44Ysx zY=Nz?4Q_?)umf&~op1;2f;(Y1+y!^TJ#a7F2YYDA{k-ml2jD^2kKCT;^#ynlUV@k5 z0K5V<@G2aH!`xp7@4|Wb3>x8cdn@aiUbOs6^z$Mo+E&v~Rj>xu!a7(F8(<^c1UJJb z*bKLj%NAa@!Zx@Sw!;p%9d^PUunX>l-EbG&4fnvka3AcU^!s_;3lG49@Kf^s8Liw0 zk8%AQcpQEUPr&csNq7pLhTp?8@CSGno`e15^E|u&FTzXkG8}+cpax!r*GT(19OU{9 zUTfix@Ftjk^F<_h5eZ(zE;ngEt{#2)gzM9A1{&Z~IE${HC*5bz2%p<8qDSUP>1l6~ z{t$*fMWc_gXkomz4$P%UzXj>HApI7k--7g8kbVo&Z$bJkNWTT?w;=r%q~C(%TabJU zl5ausEl9ov$+sZ+79`(-=dQq|k~KT9HC4QfNgAtw^C2DYPPmR;1916k3r&D^gJU*NPNc zkwPm{XhjOGNTC%ev?7I8q|k~KT9HC4QfNgAtw^C2DYPPmR-~XtS1S@|MFOozK;Zb4_a@VXVY!L6_zcEIhh6YhXra3}1ByWnoP2kwRY zKzrc4?u7^7LHH>>`!m=FkHK%?ariAf0l$MM;VF0;eh<&UAK+Pd4)&AJ^Y8+^2rt3Q zZ~$I`8h906Bkk*Okn1;it%X0roA4HGboTJCeVyCr=)cCRtOk_N)pa%Cyx#3$-%sD} zm!bAr`suh#R{M*PO@HSw&ee*@!dyOy{%fNDn*Im&_W#D#He6wA+xzV)+V?d49-e_e zz_aiiT)Qo8rf-@ZwzQePX{K+Q>6>Op-!x!L8?dDf|HU4J=?mBM9Oml&a052C0h`-^ z&28wi7tsUn!VlQ*qHpTxlSaMXSEm#{VyvsR{{XxlW$Qm;EZ6h?S$Zjm)Bhp#VJP=1 zuOwRJ$ym zXTb9r_S^+$`~O=H&KDcd1N@*T_(LQ_K{UibI`tdE>rfa5MKBzSVFZkX5-5c+FqQN- zz%*C@Wk^Hq!Yt*zWw0DpKn3XaKclS**1%d=2kT)2Y=oQOX4nLqVGC@9ZE!1WhaGS` z?1VdD7u*TE;V!rv?ty#ZKG*~I!(Mm*9)$hyJiGue!b|Wn9DrA#2401Oa2V>~T{sEK zYJbA(X*dH7@F|>!&!7=LM|1eT1^W9O+H;PTdb)iTJ0`EOqG?4^4M?gUNu5MeCy~qr zY~d+v;fLtddP;Z#ZMYl#*vGyYIVsNzpCHWs47TANo?b{&8(;w!a0M&4fjfABCwPH3 z_%L7i+W$)b|CRp#EB*gh`v0#YfcRbz2tg1Gy`c|;Kwk)jeh>!X5CM_g8wJr21F^)# zLn0(WGNeE%q(Og3ryd!+W>hCb$_k!DhIHd$#bp6}G{xupM^5?XVN>fL(AW z?1sDGZny{Th5KL+>%IGV-3t%EgP>;CAD&SgvD%GT?MAG2BUZZ+tKEpzZe(1XWL%tN zT%2TFoMc>_WL%tNT%2TFoMc>_WL%tNMs37uH!?m>GNU$PwHvY8jacnQG`Nv5Q^%O8 zW6acHwHw&MXkZ7UfgOwnb}$;)!DwIyqk$ca26iwS*uiLEY^j~&BWV69#@Cg*$7jj+ zJo$YF>Jycp!v*g90={I{3}dyHj31N=+4hr+l#A>pw@9@bxfSg{i}s&I`_H2NXVLz% zXz^LJ_$*p{7A@w}T0Bc1!s}2N21PI&ieUtdgc2x)F))#mr=l4*fVoCL=g^dMXv&rA zbW>Am(3Bc9r3Ou@K~rkblo~Xp22H6!Q)(2^Ro zqy{aiK}%}Tk{Yz61}&*UOKQ-P8nmPaEvZ3EYS5B;w4??tsX(2^Sbs#>(57A>en z3u@7VTC|`REvQ8cYS4ljw1Cf>!%Ofo9DrA#2402NNc%b*YvGUZCcMRN#9_*< zgLmNwJ^LOUh4PBl)|n6+Eb7A)T2H1Xiq)bQ;+u4qdoO#&vvw@9_^_|d+O01K9dE-FakzG36#PZ zn1~IU%Igg<4Hn?7n0BTf?Ps?Wsn4s?nZmw5J;FsYZLM(Vl9wryA|4 zMtiE!o@%tG8tplb_Ee)i)o4#O+Eb1896);xpgjlBo&#vl0kr1;+H(Nyc?j*PMtiE! zo@%tG8tth@d#cf%YP6>s?Wsn44xl{;(4K0v=K$Js0PQ(|_8dTaI9Elvb?`16L8IP- zqwqc)gAd?1`~^OQ6L1nf!Y5P+Lke%^ z?qgV|1N!b2=-vf%?*h7a0o}WR?(x}ha07Sn08j7&Z{Qn{cy^;cv0~-fjruI2m1j3r zp50h^c4K9?&q|F>@$AORvl}Zr16H2hSb271<=KsuXE#=!-B@{cW98Y6m1j3rbgv2B zJB#j}Mfc9Kf9TEUb-g+L=FKy5Z@!sAea6w7HHWvD3-e$;EWj%-V})7He7ck}m%(ya z0Tr+sKeh_iz*<-b>tO?Igqz@I*aVwl3v7jLa4T$w9dJABggamt+zGqkF1Q=+fqUUT zxF7by1Mnd1hv(r1coANLm*D`s0yXd|9E6khujnzgVpgm0uh5sT(3h{!m#@&5ukgpc z>38m@*Z#XFZl=d|rN{k0_qe)x>euFRo&R5WT-UY===8WQbmMWIM$3+&WyjF6WBbb< zKG)$L74n2putrgz7UPRPAqaw@H}ruJ=nLw6UOxzfaEO3dQ0ILUAqmvE@Q0JKQp#TbD6pV(kFb>AU1el85z5%8|8TPB3mM+CJ zSO&{s1ytYUtr(bpOtto{KIqjhqRpiL$&6-iE&+vHLk@P*J6!pvBtGn<65k7 zE!MaeYg~&puEiSHVvYIUPuBkd&07a4~aS@~RK<#Umh&qbuwgtVHFmRijy>wOw&okv>dk=A*nbslMPk`amgw|n~k z7kf;X@8w_X-jUjWXGfWNRz010nx1}|x%LS9uJ+S)M1=itx@tiw~R!&9uoQ>?>Ntiw~R!&9uoQ>?>N ztiw~RbF4F-qz|8@51+)2HnMVUWaZk(%C(V|Ya=VyMpmwktXvyexi+$LRXZ~3iQqx{ z`ceA&QTqB(`ub7&`ceA1nVGtonYx*ox|x}}nVGtonYx*ox|x}}nVGtonYx*ox|x}} znVGtonYx*ox|x}}nVGtonYx*ox|x}}nVGtonYx*ox|x}}nVGtonYx*ox|x}}8Bg*w z_OqGUx|!L!nb}(H6E-tjH#1u|Gg~(^TQ@UX^Q{Q*3e>=>a1ai2e;vFFC(&2aCVquY zJkQL1o|*eRGxvFBZcgfv<}+x7&+Vttk&AjiF9aP4fUazIsYq1hdScxXAL=#qm?{I?N&<8@GFN8ur2!n8lfJp9*f@p{V zp88-lny?y8SdAvEMiW+}39HeB)o8+MG@()I^js5GqY10ggw<%mYBXUrny?y8SdAvE zMiW+}39HeB)o8+MG+{NG(8O9au@+6NMH6e$#9B157EP>06Km1LS~Rg1>(YdEX+k4w zu`o?om?kVt6Beck4LydHsYOF;(a>5nv;hrmKtmhQ&;~TL0S#?HLmSZ01~jw*4Q)U} z-$6r9F%E0d(!*%!VYKuxT6!2QJq~S^gLrevWlZE9;h4p7GbCJY6uM5_+bszZ$FHpX-tW4eto-Nu-1 zV{EoDHrp7RZH&z}T2;yTX=D7fF@D+@KW&VkHpWjIT%a|L=y)4C{{Q|7(fcWLFFXJb!hU!jUVsClr%)qHND_QPAz z)mC)16b0$%~Hh_#tgPqQ_W}{T;^Fc50+frMjT4f^Xh^jZx&r zGa)|jmDMw{XKZB8$l%;yw}4=`_%TKw+YFgydoMWsWQJ?TnGEB#OU0|@$%isdW=QYM zGdRZT+lz(qIJu}iM3&6rLy%dP-0aA}-ZEmM47<4@>1;2#THR^nUOIexSsC9Ks_(I! zG~#$7#Rtaa9G(o+cf>!|5nslcNzRHo(jU=f;O#q?VhMJOj?WE_?^4>}%$?Wo%zSLL zQ>m5_nU6e@xkGl8a-^e_`KpwT_`5seXL7o;GrlwZY(AIK8Na?G{bF_MUgcv?<2zel z>xf?_|H|iu+FN}`NBlzZ3Z6@Q{5>7UQmc2jtC&G@A6}I7oV{I=;KEzml>Gxlhm3>jp<)vbowQ!+~C@-hy zSex)&sjp$>6390@D>Em1KwMmOwBt%&1O)~5vbyE!7i+XC?vcaUgF-T+4)ZhY=58H3<>vJ9;nN2X zm^vgXYRJ?9L#Kz2Pv7R5yWrL_V{VYAN|*Q^&|BNc4*oU)W>qNL-P{R3|dc5oAp{M&S}js>!mc$ zjGx74GF5$554}nc1*>yVt|&S1>ey}ba&qQw8)Iu-U0%3y zRB1(FdG?%J#*E%FC)?}+b1c12o)+?CB^-bf1*jhHHs{`Qvu)c?yUSkQJID6^)ymNI zM$W3)F=;1EeiiOOb~CC+CjRM{r8M5SqF{7k-|y}S5X zd_K4{zB8ZMe4el~UfDh~pT&G408XjGd)h=H}+kyLIgKcVjV@nElQSV`dOT(RI`{Z8YZOWRks-)zj7amm&t3-$j?gm^>eprt!jPr0hc zsbv`PaUy*+t>QbQdgxZEU9o1ZlWGqPowWa3#q(@O$Xtu+aWj5CYfBZ6&mhxvYt8tX zJn2x<8NuCuc8Z^6|3b&(4TuJ(d}iCV4R++SUhBNDDCAr(mFsEXuz?8IsLBdIUqbLJ!Ez6e9w%; z(IKIs17hSkTkOcCgB|wt4(_&chL*2D#aYRgTEPFdy(SCpx9yWlF1@9`!g+#i?+_c` zS)gk&mj351YNB=n-DJ7`jw91LduWNfb&gx5%@yiK`c+Ave)Z5&Ry&Alo{ule=?L@W{*Eni`C0=ilqbFz)svt*hP9i_}qm?7^Tvu!>WXX}_TTlMSMl4S+a zLvP3)$;*&w9rmW(SE(3!}9*mEzBkOWqbxT{>N%*{ATQ>?PKM_AC$lGrb;xi-Rl2nFDNZB zYqp5*$WR_QHFKGywL$sFDSWz{X8_&TO*ycpR=7IGrLy;$PAyqKH7#w*dZi@=%SW0@ zBBPyMhi=6=YSmDO#;{uJ;#!zCD34L9*9MpQaSo?lQlCfu5R)4bksI@e7mLaZ3d)OY z->;VLW&N^aVzT>{<;*WCnxA94s4`b$K-sL0JafDHhK@W{8@tJKch@X;n>kvTDUVtD zXgTU0zFdOAn3dSe$JRZU^=+H&Pow2vTh$K_u{b$+Gzl*oXy%+e3puI((!k88CBF@sA= z2FDaE89HJ`)cCMrNdpF(F*`i-2aQkapPm$*(l5AQ%E+9;8`5%;#z#da#YM#T?H$&C zbl&U=W#@FsT0#>Y5@H@i*Ub1M)QHW5Yp7Glvg{h_mgJDwwbZeFk!IF4O-TvqnRQ*t zCpmRIQgx0m=f7NN-`3u0{KohYYa69z8OiR6TL9wCb~q<$Q60r1T8mh%r>&OEFe4){ zJfp{2tD z2To3_m~^}R+2MYF7-svWzu&zP{1ceCguHN9*CKbX0keh_FBxPs^y=GdtcS0!M`Ce; zElQprvveTRR{Jj3GYZr?Mth0MK7GI1_B&&+@x-NR#`QQY<_ht8v{t3kyF#Y#+OD5w z`bXO*WygK(ix2v`H*%sB{+^Lg4}PLkToVTaZDyx<0jl(O8z<@txMA#y7ac51I>8lznk%8eDi^t z8_jq%mzweOyTq%x)Qq3Wr+m9hKU?``NW;wMg0{6hOYXi69rI_`w!GRtUv}3fi@#%% zR9a@1qGn06R_g!JnYGGO((8y{#y2W>cBKDjNBlzNDKOSmUsoe-dOA{O1w;pA;qTi1 zvFVePGdJ1(j`cdsew^Nu;TgH*HB!>okrtg&rN&RpvhWqKH~v2CcN2dX_WK*pPkb`$ zN$&fH(N|M!gj_nW$A!9I_eYu8A99+mvCdbo0L=LLvZqtLlDio{Q@T3Et9~-$XUkG8 zch!=8x+Qv-GSY%4OKT!!uq7ZqAj=3n8fg1s=lg-u|I%9NIpJgbgd^_{wnOC37*x~{Vsuj*{Z&*#(Os<+H|)kZUZCa<0Go#|)u-)WR?MNzYOYGyIL z?Oc{7ZrQn}1K}1WH{WjCcKZVPu#NwzQZ~^Dy>xEk#6IPW*NJ^p?eyKcb}JpVyO&&* zJt9{TI$*|lhnHf`K3&unhBqkI@M;H7qAfUpt=*E#H zHxBPVX}~V_6PHXHnp_kcTa-*Vd+VC2wYCUauS+$)r8X>Z_3V3(w%2C-JC1m(EGtzj zZ8NsnQOC4vu4TKheyFyK+Gm^}tF_VGu_x%LPe(21mnsRHvHLq>XXbatHg~jiHkPb2 z{^uR37Z;kbX-0y)(2;tXd8Sca8baA0ox&Z48wWnu2rjLfMyy31_`D*^(x>dsC` zD2$3K^mKIe@Ec1;-dN;m9J4)KwPx#Vv&YR)`7vSa0-T^u?N+#p_tL&Iq<4`+N>&#Y ztuD!%7gZWKI684ibo7wK=)r-dQS&^DD$%T>rLxYpFCu z#k$E^su$bGpzfE5j()jp40aiWuF;Ibo>^VhI7E$2?YhmGqGs3FxSxStEHeIOMGmw3>mR%cz#)ad}g?3(vcl+3?H^? zWL$n!RDN76HORj`T+RyjELv4uyl|j@WN;rDZflx9cX-9HI} zwoY(p_q((|j;A_GF7xEy*iARb{WRh&oE3{vXT9{=O!*<#A{|$Gz3mxiGj)Z8jL0QR zE}iH)@VgO{LwmnNd%J3pe$Wz`9qHD-zo5NGt;C_FtA`ieIAZwX!4`{ct-LPL2+K`K zDvXLLN{$^8G%~u(b68~wx?H$?gm<4FGh$NmBVvZ7rWVJB^eIVHD{rMM-)W7$L(60i z|6{FPqrc;uPGilT0Vi4T(5KtFbSUE@XF1-)@E!kj>z+M`;j^yGG}_v#d7E4IL2`Du|o*#}Uw zQ+&H2FgM+AD@c#X3{6g-lASX--8QO12BsH=W=8bRosyA0CC~O3mM}tDr)qzzTQ}e_C6ChqjO+Ze2{d(1CwFWqe!Y6AOLoqCNivN6JyEw%5xmFP5Q()Vp?m!D4Q}SUw`~a_xe$q3Nqo33b z?fs;^`NwtoIrdNW+Ci1PlJs$W6O*3V9P8K^+a7U9=da?evi$q@dQQ=~BmMU01?IDY z_6G$@f$1IcwIpJvlm;i7I)l}_d`xsk=|}cKfhg7o`6*%j86uOivL>bTN5AyYLf4!r zSpz0#s=k$B>Dj$g88H)6Qzquw>MsA=AA&{c z+s&T2K89 z9hs}OWmx;(%vIYYk90o8Fzcm!3A46~b5wu&*#Br*OXFu zywuim6#@6t?+&|al{%!UG|Q~Xq7;WTEw|CXW~>{lwmXrVTCe39v0CH0U$3#HALtg` zGvI%*UOQ+!!CJufri^oTeA~;$7F}(vs2(T$Z_BnkWV@%E!YMtRmj4&K5&=E`7rPR+ zQ?ig1>!oQ9+xAN&Zc$IG)lA$I52MqSEU1XPZc@ykKBYbpfob{LUU~)mKQC*%&A z9yT&CCpt1lN6hyOj1L{zuU}ALQ171p;W^1^MKOtqBSS*?U-dzL0g<^WBPXfW>vDDL z-!fZ2R?TW=Y%{gtS^Txsr(GI9Ua$1_1Jd^sZ3)G)9HuR- zti@|GU&Yf_?nVMC9{*#6YH0>~!_2JKP+fP*9Uh!C3g%>21gD0lXH{g+_RN`6D1&WH z(HS8Fv!%c7^C5Gxl^k_0S__WoGUxDp<(=_r{i5Poza)0Xs`_?_L>ykkP?(t_W%&m!Hl#K`HKKg<@X`NOQu;&8Jzc}iN2ShW`2 zt9SR!`l!7+Gxfq*O13Tp-y&PpTjq|v<;>qU{>CxB1q*<*Ipo%T0M}; zEg(y6m+O7~!+)&Y7In+eTcWmA{&DEGm~GtkYbj@Q07lNjK5%dp+3WkY>_2kI{=AmG z8Ly;l#?R+zxoU$MujFjT&s3|fj`+^>v-yTMd%d@6s*%#L+TStIT*+Qx%VYqV>m&RqZlk zP5rq%?d-X#O;}{9{;Nf|`}lpN_f}i$)-oAwyHnm&mM@ps9?H*8C}V)-Cn!njyLG+J z>hkBzRvwQTt1N!EyZG%E?y}s|9OkTRDYB~5-kbTVcA2Hkn(F8k<1;LjI&U1OtUvEtx+VoIfUdVk$qM8hh=|8zhFupy>m$W(-Ulkd<=j-^!ZUws+kz zQLXf5x1^6_*Cke2_x6&_!@*cYbzqPsaQlV?6DnR+fYURMQu+j!x_d-imI9^Sk~wQgditmGt+WawaKMb* z#kp~pE;xj%7Iq%WQ}*y959KKj;M1!4d=m9KJ|REv$Nm0p?Op)#SBE67=omd?IQ2sS zNv^tbFp8b+{yf+G?;fdmA$eS8N5{QTk=r*qx-T57xcV|Sd`5N1XCdFq{Bzc8n3x}1A$2P$_3WctDXBf@*vllPl3rFy z_j|9A&I<~)NW6S6T|G~`cG1hc%jN$4I_vE3TRVjRLWTUzkL^6mcdpnJcY1ht)zwRP zz2~WqAY9RLN~L#q?K_q52e-U`Y!_bbd-$mS_c?Rb{=bL+%l-eS-p{3HyDlKEaftn& z^s~LoV&%UZmj8zx>v{PyNv*d9r*lo;m6NgB7O9B>apA!lYd@`M_hFfJ)WByR?IG`% zMaG-hfDmnO?2O&7H{V6qD!W=PoJfeOiVMuK3kc6}X!^jT{i;IK!Sw22f<^2i8 z$tlGNIdSV^b0;K5L?k4JMok7zDJ)KgT zlvI+MT9TAlnnLbj;c@ZuzQhs9$;F8Ygd-BKONfYwPY91lQ2DB}=ZiSWb#1#iqbA%s ze%!VR6Ss{UxAnRKW0RA|Wo3;^N*d=meP2bzucl4=)yj%})0XG0nlgD+Ztlt{Qv zGT6V^xq9`1p;N{;iI_8B-Q7CJjEa{F-|7R?lK{Wg(yFQ4Mm7?>y|(!%znq zLZuOr;9t-`b8Ohe#Pt(~&CdzUOnH0n0=d3e`bXuY_vxRVnO#&eYQU_5lo2D+-YY4N zUQAE*W);y&9r|&WA>Mhnwtbtb%dx8L++4SKI^V5r zA93ZuDLJqBl%?VI#l>vmSXApc8D*Zzzz@j9d)B8i+)%F2Z?@TQ&X7%ix$#^1rmb+~ z$jHSn$U2+e4GN^zm$)y?+@W$lsos7V%(01z6}Kp-JU%`$@as{PRC-)`N|Ac*nJ^^G z;+K>cH+tcK;cNdiB)mK(qnBLgsCIs0^1y!n!KHzHeG3;4DPCG2M-7pMwinOQDfEi- zlURoh({=AG^zx|#Te51{@HciGN%9QOiYr)<&-2E(d>+2W=?Af%!zzZ)ozJ@L(>}qG z{sR{l7q2SPPhSh8V+z#M*LHpRmqS{D6FL^^oSAJ73Ph=QtFYQ8UMn)pnSma|j z{><3RQ2V3pvtKUHK0BVqA{n=Mwd}oeo@0-yW86sp(xroAOEN48xuMy!2j|bq4$Vol zWR8p-w5&9CU}WULSTjt?%S%ZfIM8#ztik;!pXE)2`TW5E?m*^Ru z8P9|5;j2by@p_q0wUuBCWO$#TDF18Pqn8LV(!kC!C1RiVCk9>01 z$iL=Mo-l6nn9-ZZs&Lb|^wNaH()5hdgoII^(|^6m%gJ`Cyo!E&_9)9LL<@x;{37#(}M@j z$Szv@%R_g**CO92`|*LVS8)a(m_sJ+%Kz^&?)!Yb?Goeui{ssld-U4i(-iIFzPnm? z4#AFbFMUeZB~j_jv=IsMLt_(*!Yw_M2gi+Fk~3n%ks%S~vHb((R?mzqWv_l{;PXG) zC|`X}P3x#Rs(x(T$Cp$V6(8DhEXgxsKwRO%g5t`N@k#^Z<2iy_sK))gc|S7lIYXsC zVWgy`Kbxz^{XXegY5V?5+1zF1XK4xj-;DgYQ5E@dqcV-ef&Fsl<`>M(?U$EiWR=F{ zSB#EV&&1>7)ftlbwEXclYXNTSxUSd-kq}7>bH01S7vg;Q zig49`5a<8dk9@_Bn5TJ`l9Lg5Eh8t0T2s@yg!AvZYX#1?EdH81nLfRPqoap-^gOYJ ze=<&X(@UgQ`k!&Ksg~%+aT1wZn)*lz)WbVd3yYOH9y99UfSk8FL$uPhIcChZE9rLq z)i)2F(-u|ebynRvzQf<#Znlmc8vK4PL|9;Gz+U;lz``5nw1#4x(P&Bu)vcmEW*Lqq zss>;kWH_BsdO&f~%NDieSj9B*nhF$`AaZc;o_+HcQgUqP%Fw0NAAJ;$TL(7Wys5(6 zvw~({RUkTHy9gkI6W#Y_DUp#P3LRvZY|z02GC*^{35XsaviPhCzdlY*;v!YM*YT}I zH9Fqvs4V6SgOwE;U!w0o@1FjrZ+C6WA{zHf?~Ic+FR_kGZ&mUwYfE{9p+e0i3Rfi~ z;>Kqa6BDfiaceH>(6rz&`x=us(6s38nU2>ahJtKQQ<~~p_!fdLX{4IjXOE_1#o;@* z@<2huA_K$^*k&fRs4ko~u*jO+3N-rK=h1=;e0HyyPK92qXMJU|HILp^Svua#4lQ9> zD1L_DOM-q6T?#6K>;v(*!v~ey=PM4_9e%&V4o0OG_C2uE(ce~5@uFB*h*dgML8EDg zgGpIQ#=+!|WSQBRDz_l3#Z7Vru=spvx#pcy3Snq&ekgxipb<5>*!o!G4Q;DHuN!{bQ7zbr9N$>`&mh@zZYLcP`;!WOQRUlB5deh~0WT9y~ z5gtiJil;4gO_d>Aa6N$E*RR05m4{~|)iGfg?Tkfus?e@NHk60`byFF~Ngh^ploz)s)|8-TU=O-QHRnQFa|9X$c1N%2k z!hswd{C^i54_!FYF%=)%{!G~uLDKQe$Y^}3(e$xddME~(7grd za0QAEPB!kJ+%G-Gl}HJEIL57^+T5+}+i%f#g8d)mo1(>^lG7Z1R|nLC_jwU9%}MWj z)obqjZpqUEJS9%){W)TiA5%}3>p1O-(YhkwHj}~Y^YfG!!AT#HQ1<={H>al9{ab1i z31;qdKfg6LhWo0;BjoMHpVIqAs)N3LYI>SN_bxt4^xOxOs=T~Kpd(4Q=l>C>``CEv z`mV0^hr*{Xjf`A69Zoi1J$mE^*jOM60yWi2P3%b__dV<^t*~%})oEq*4<(y_aOCLK z=A_aBt!YNqR5y4>-gSy%LdlHb@wgK)HmP{N0(+FWjnilfD{$*}e~+d_ZK%SZL0zKh z=}$)d)>TETO2ffmxO7#Ky2>BfF*10<kHVj<-};1@;!)d8rr z3RISegMv$l#t@gmph5wMX-zTM4k%UXBDI!6(W;A9S^be@|JK#HnCt+k1OAjy3}7h$ z6-Gt|k5xN|9j+6DBcOH{pvIMzlge1EKmcx}t5DZO_{x_(~pd4)W|A-6l-U9I3iSg_ou z@VeqFs0d6_l>eRkUcNt|Gyf}K*5Rb)Y3Ow_%;jr^u>96SzL1ta&HqmREq^^xU}lyV zLi?$@&ic7hFPC&_X%{d*fV1jH3}PmI?%+4bFh`}WVzOV`<3whHDjP8mL7H53SP+pGxX zEVQWlU*$P#$)0V3LTn~)kbTPRQW30LED}j;K5Ulo!oyWaZ7fKljGK?5X$l3r6DeIgp0i5@L(V6Tu(z>4sJR;7L83DZJ+vT zzfM0@T3!^N?b@=xv0+a~w9noYym!#u=JB+-BaNwmT@dWBF`j$%?UIt7sp#n8_6}4Y z`qLeO4x3sZj61sT7Mu3>Y}wanu{68AfA87g^KbChhVKi7w)h-QFV#pkwYPV-Z)}(F zTWQFx_X&3q))t;-HFc7Vty4+6!{jJYY%q#kl^07b$CXMi2}1o?D%dHO_g2INcYT$a z9Fo4$xwmoChM3@WHhE$jbe+caePpk1b8UP$Y7JMl+uXI))j_K?SuEbw(mG?`U}&tZ ziCX;Dp-P_#@}q9?DegI}STt%Gc0&RkO*X(H9fT4m5<_s7j=jjSSvhE#oRPjHhcWMP zPftHTJxx~2q036tu@q4|?)*~ipmQj>c#2#HpSUoVDgi+#h;m)oVTCFL8I4ydqoF2Q z=FU<`3ORq++u8x+@3zL4osJG3TjKz`Q9s(g37;~)keOir9Rf}i-bEI z4u{3!;7#t9Xk?Qh-Kh)Z-U939RM_GNm6D5}s8!s*VjkBa^IXn+R3i7&L`&ySVk=@= z&Cx9N<9d4QN#s+SKC!j;^kqd@n4)ZeTDqnyQ8wtj;>VpNUivYa`qi(bOTSlNzO4Q|oq09nESX_Fr*Y;>`sOBd#)(rN87}%$yrO|$AQvW^rO?Isr7>(QKL+lz_dse|6sH;+>GgKQWW%sEU^WM=RY8*} zcw?$zQaUx+F!>Jrt}Z4fz&VLHt`iAIa;m{si7id7xw6)34OKoe*)T;`qi6&E>8M5^ zBXtmFek!%Imc-AD)~9C@#L|6#sK>fh%!$}<*;CzPYjq`?gr<(pCgI`ZeTawe@z+N~ zc`^Gv)$Twb8?!&gCKo)yv37h5vpM5yyCi2@-iW|vtSP{Y{9Vt36LobH5BBsPL?)(z z;-HHFxOi^iX`Bt zv)|?WolrM9#a8`fYPPLw!m_2JJ_wog@8m&m$_KsGh;1p6R)vns>!%90wF5BQbsO@ON6ibMnI-2cNQkpWM0ZeGH=QyV8?z z-Wia7%W~|!Gd3oDDx>`Q`zQ`MO7AAOp=U5pJLXV9#SD2x`pxVtv4UmIQjBtYoLI?i za-4EIErk;FDV-M$st3r2oL6o^grBh**)xuJl0BV$(sP|;;sN>*X?!L+rROLGGZmw85M zX|&eHU_yVTPzBIWqd*1?p9k<=Gc#{V-#*LW*O4;vG5Ho7VdNXk98KCXc?M^#ff`o@ z?<+?OrkpVUg4JnP)NViICX;RVBc@)wkM;9WJ zJUZveY@6gp4zW<9iG$ZN)>&rd%r!&Ca%97o6(R6)+I23J=Ku6NX8RZyQvHIpM75oa zsr4Jn>Y%Kbv#1uB?>ekpB7gna;fFZOL&MKfiRa}6Jo`V4GJ7@_eQ1~yqR$S~{&_hQ zd4xTqazt5q_bLvzoGG0>1FiI_UQ_)VJcUgicDGA6$Y04f|Gt*LKbNs^ku$(hh6Cl) z!;dMBl$=X(ous&CN~JSclF@WACA42|&5YKr(Ks`Lb8NQFi7^IG#L1aZZ!Fdyo(a@c zxD4F2y54ZOx2`&56e?Jc%}AuHQ)G5l50W+K9De<4`FOh~>2M@#$}20&jb<}vb|zh} zq*K0bwdf7hyB_K&qcVAkdl5S?6_o-Rd6*40PB=pzli8+iw5B}Hke9Ett;I)9F!AN( zjZP15VsCO=>&ahoPq7ih#?4C(OQ#Xn$DkCbt|5*Tqp*T4=NhaOIYFN4RZA>0z!DK{ z*A_3c`pZWVhm^TSeCfiHVWewth^&GwRkq+~^jvHy=rK=%67eA+Sy@}+;466}U+?TQ zd0V=>Zk6jAiq~q3OLTLcHZJb<)YHBoRuK8{AzhWT2W5+_n3JCfY^)bkHMIsqDZjc( z>$~NP`P(ZEhNxXTx{TVp(%e=^CYb#z%`=6FNTtuyd{KxfC;7X~3y9HVcKk0m z|5;1&%!KbUYJS0)%Ie886`u482J-wXB)iiXv3Y~|HRZcJ_Jw4QeNR&y*$L$53e^zJ q$?$NKe^mVp_H8;Nma(PAJ?NKDWtQb7q!UO;B^5|-)R54tbP;JGbO?sfMX3s?h@$AO+a#>ImbIZG zXhd;wDI%H(mZ%9J#6+?Y!N@B2e$ULgDIl)w_y0=rd*+_ZoipW~ciwqt&fFUzg%I8Z zPC^XG@7I6S-HYxO!iW+=ZW}Oi#OSZS|LQ{_LOq4ZiyAO`Y~NFRK5Z1j>PniYM~v>4 zvN(9*qojS8T&9j5lRIX`BXhsz@2UJ9U$9`>qCDp%UP5@hKmkK%OYs zzhdT@0}FQvF)c}mNddD9r_G4(KQV~+se{OvO@xnAo2~ragTJF@FDO~ww^w?$5N&@J z!ujI-qJnAS@xVLCYB%-ydBL>hi{!)7Li&!R4_i2GL19wQiPb__)ca!=6)i5Yx~$0+ z!oPug+Ak_DTr}Hy??%$oKH;)Q+Bd%7bRT~TX}62Eyb8Hm>=s^Pn6Rq9TK!m5Dsrx| zQ5UZY`yIAEUXNXqM%}Gax$1a79d;DPvRSiBq{_|EF+#c+6LiihPPYHOub1DuQ`+{p zBrKL16<_)J*EaQ9l@l0GIqSS0vwX*&R_-@{oQy%_;7o>FbWTIeS2M+ zZpm43Uf=5^q?|ySt|FfIB6v4g1PC95{DhDmyt*M3ml`3hqlI)T6VejL9S>jcjy7Gn zV>5T07SgpRcRb1+7r0|6cRWFhJBlFc<1IYN@jc$p=KW`QKZN)1r{1bA$&S}l$7_n? zbr!V_=Gp_iL?^;<5k(jyx)COeOu}q2fN-c7LpV-MC!8T>6V4TP5Uvp$2seud2)BzJ zgufGy6P6+wDV`O32=|J8gl`FICk}~^2~Ud83BM3u6DpY*vb*d~J$uXEgp(LM7CBwc z6AleSE(U2-ydZwI>Sx-!L%r|h#`RM&z;P{+^^cBggErMVt}P->yz01iYL&)Wfe|n&G4qbkc|2WDk z)^`?(h3ITD_a}=ae!G*$OmZ&LHCZffA+9U^+Et{`!?#|Qawd69BYpSZC~Xv_&7wu~ zNnLzZ+OS{LERS**i4~-4l1&&TCqXjx9D_6pd2QXgFfo$UMd(-|k}2WcTrIuX#Hluh z(KkxQtz|gNA=xG9XjkqkLi$Q_EmEID+|@B%kgJlGk~$-h-gFoF;xEF;?>}^mh?+mG zWFb1F?18`9-%eaPw~AF(tSQ!|pkTowF-C`ju`LB6fBu|V(?qXD3zsYqS;b3> z7mAb;b)872Qn=_)>98bM6*8|7Dh6K4?tvXAV`UCQz*mJ&CH^whT~}@(@>MEmbSsc~6Q4aaqST z$J>9<@&8t{7kM|uClVXLJ=9vd(87-5A~9aVDxLLT7o^n@t9s`<@jYSFy{e=jWp{b4 z(Y1V#da9cJu2m`}9WRhrE<^ktyVJV1Dqgi{XLJ7;VmTT!Sq!9g0(+_QH|wj?s(UTm z5#UIV<#NQD^=e9^a)zXgO#39!2wX3UZ2aN^jf`cTer4L+OBQ;WZR0i4Q+q+cJcP{ z4)KolPV*k7_o8NAEyJyME0@P-KAh=_>fh@KIHBF08ci?}1=v4|6q?IPnMb0Y^uj*7fD^1aA&Q7%zoQQ1+0 zqHc~Vidr4DJz7Q&j$Rf0Md$9F@9+G2=dU|oh_S{@h}jxb8QUp#O6-={J+be_ejZyB zC*l(0ddH25n;Ca!+>>#y$Gsod5I;D+Fn&$^WAU%VACIq1a8Bryke@IyVRpjOgnJU6 zNhnMBFrhv%II&OSu*BOEA5Pqt_(kH^iRTk9C0^;`(TLV1TgW^^zzjW>+#m5pGx)Hg4ALbVp92OcD5!N{@CoDg# zB<$Jnu<(fR?cu+R@QLt`@%_nQMi7(~#!QyO`1`wbmjNP(|w^LPn757!fSIM7+JD=0CoJqtw+7clvm7|eWDy2orU^zf~Bf++^qiiRA=%*oaC^Ai_UTv_@z9JB5hl^cX~7P{tWzpxuS@E2%*<+lHqb8v-_Q5jo8Rc_8?yBBifqpVkUS7 ztN9Z1Xc;ry0cNSA;sm~FC7$Ui86|tlXgNZ9Nq=V2e0sAV+LI;c%WP3k50AvoE|oo) zjopw|ksO6~1<7U7Rs2hKlgv)&ULfocg1-?j642b9A`h+2 z#mnw12jP!Q78AuJys~*>u=polQ;Aq6Rx{(=jy|s!_p{!(SKKH5Bpwrg5PuYRi5Ku| zUuK22SG*zzi`T`6;(hUfI4-^vZ;C4B_LYp0CCuKdSk0|vuG+wiy-xUxyP2Uj;qh)` z7T=2R|A1&O?qRL+5Hs2iW|xOqxjc#|^%us{<9I8-XI%XmpY5-B9Z%vN|4pQer|=)2 zM*E)=J;d|)jDHus#EYUIGf}>HjrnmOtH%BKVDE_G;%#vg^V4W?P>d4)5aY#RF;$e~ ziyp&Y`&dj7AK^(>;HQ3yPx%$T+ahrqU+-&in>fSlx)k5#Jyu#r{Z zZI1jF*)kBXg>f4;YWE{ z#oLdFAMHO&X}?2ZcykXp(nqkXq^^VIX8N)~=cn@gCE1ky>$FwxS$2_TDEFFllrM=K z2c3BhmTw5JYcU3qW+S^}SAbXQ*5!uq5QYU`|5^+W;c9sO68Vt`YK?IWdCq`YFkQI2 zjT6CM70{hFyg*%MQrC&1je)h2MU1&P;ya@!lSF&2+Zptq(T4a05zcjpF6vD z@Pkn33~|r}qK)UdN3mPK21ySQD92E@k-|@^ywxt%0TUU6#SRkLvOO~L73r2}{`L`R z>_ueDM$u88WuDr_IG!lF%UbfjSH#edR?RW`A(=j%%DwCv$t2p5L>a?bw+$B|@@CNw z!gT)iO`#Z6z%yyBpPi+l7*Sl zh!t7J`?O<=2$zv!oWgij1R3t6oj_eiiFn?3H*OQ%jR(k|dLp}22WhRYo8r`KtcsUM zkm(@u`!j8Pkg;zP(CS*n>A#U&4ormDR*YzM-8x=fhdW^BO~lGCk=H;mQKoUN_!RcH z;wG+Z;3$NnBi(p)6Oq~;tG3ck?LCs2{SS=4>-@;oL`I22(vTMBoay2@>BL&IfZ1&u zzDH+q1#fHytIinl4RgFJEh(goe~QD>o%zW_dg{6TM|>PFks#ZOMCpxZ@jf%%zr_d2 zvtjn{ipF%q2l7W-PAP9j2I4)m7c*rrwK~o$K1-D2A9ZA2ex5l#l@)uMNXMrMm7PR} z3}aUO3Ew6{Wa2qRi7ZheqnSr%ixYT0G0b;yXhe>l^_c&dn|p{l*;VXAB0bTxv+N1x zi#hmUsbU^8?p&FMpS6&A_b<%f{e=xpE0P&_CAIi9lDT}5%u;(Vct5wv9Cqsl;h$W_ z|LG})$X@tgC-GF4;;SrS7q5@Z6~km-FVyiyn%Q*%kfl3i52)H zw~Lj^%frK1jps00)HBbI#q%33N1&x6ng4&5H{rL9$G;lQ{C@|daSeXVoy_0kn87FD z^G%dDGq2ym_`C(r>05cL*vr1}t#XPuhd(e)PR9p%TNdCW6k<32i0|Z!-{p`06DWf4 zdXzub5g%f z_yN4BHu!ixc%$BUlrOXE=OH}t<)-2h^}+A?2OiohcxtbSzu_?{f76-$oMre~Zg^pL z;dv>)Zascbja-h8_NH)=EAUu%$d$rN{7W?8Qz_rK6aMQotV*t2C0yn0ay6@%JJ}an zE7!^Oa)aC`?~-@RO>(o`BJYv+%KPM2dB5ByACM2q?f7|>c$@d(UwtB~@shs4+xiSo zu}VI~Q-dAy5o_-B;=-kcE=v~9NlMPkcgbBat)RGQp-XPjtfGa5^W5`h6qQUXC@5T5 zV$Cm@#yh+e7fma%_S1j4_iKLNrJv*0e&(%amMJOj1DcaK4V*r$*cdd}WsoE3pw>xK zGps|W6)Y(!v<}sIn;E3`wGKC9hqsDNOSkk}IIGQw*-IAAnpV7I!Tf1UN?b;mxmrh= z=|{CnpPpeItz&hjDal#tHMw7QKkJy5R4K{1*0EX~ZN^@e@mNQ7$C}l39$P$T;VkQT zouSA0f;q(nOBT$WU%1?D{ERt;#f6LKEVhoHRXlBJp~uZFZ(48GQPzq2Hus6mQn5~J z-39$zra4NR=IDZgmXtGeWvzw!#l5ik4VOa4J%z0Wnv&{1t2v3&Y$f|Sb6w^*lFn(B zG$l3DA?-}-d{cV!TjY|QnrB^T#x86Xo0efIqz{U&5>=6zyOoVl)sw}o(x+!y7wcF@ zA7oogTHZ}b?rU9Q%6`dJ87^^DcZoyxOSJ5l>5M#|e)~|Dx>h~AVS+3q+%y3(%E(+)HOiU$MI7ero#gf!F+wq#> zcuiHWDM^`WM&2khDmO_-WhR?%C8s1g^;=S0q$5+3)6GPE(_N+&7Z)vCvPj1#XJ@+W zzl&9=j;Jg*9W|q9*+NH5wiz?OaAs4Q44qwSk{&ThsY#h`T9gGv3#PkiX{swnn!X;& zjHD*zb)7a}-_Nz#4B{MVn)1taE15HYMq#VF)65jfjyqBuDN{_78fm= zT^MLup{{*L<>n3PH>T^-X~ntoOOlfPm8JScY~YNd`SYh0{~})b0!(}CHODenna=>c zu6YJv(P`MDoma?Ce#C-l#q&h)f_V$(F{7xv)%$9{*U*2lkKJa5aRPhq&g|e#yt|J7 zJ*#QSTz) z=T>sFc$!ZOo!RNH<%z(z_$1EqIJ0yawe}Q-Ctf;t_%UN43&Y6_{ ziKnoT)=y`)ZryrQ2EPoY=Lg75x`e9yTtZcT&YM*XAu-OIRgC4LiXkM%a#6(?Q&kM1 zs>@Veiz6yV98ocNYN!AAYt&|f%E@J}%E@J}%G>!}6+=jj^Sdg>DOAM}662(6ZLCl+ zgv1ytbdAbY4ErF&;P0JAOMXo{re>I-bY)#cu4i1mho>fnk+?Tpd5*D{w zdjFu?V7Gj?UT#@BOwnOttFW`a4%1<<4$bs_`r1o}u5OGQ*PnIxg9_cumwX>XZ58g;*MHaHQ##zKLo@v!Tz7EqHXWMRJgp#RgXTrw>hwE^l4*Tj*r{_L(K0}AD8LmjpHA#o&wHd~{MiC$C8bs)$ zLr)!=F;;c0^AN7o$K~HH7j;;#!x|l)(qW|zPpHt5^0E) zmnU@i7af{0X83!Thj@3Z4$bRLF6&%w=ShwV7wK@W4rl0avJNNcaFhF}Se z!guub8#;Vdhi3X0^!3v^+-3a}c|NMc2X$!1+^etea(ULe#=266C9T4R`g*nwr|WQ% z4$bsut&Ah+mTZ?$qHQbhtx@ z+jO`^m$gBMcj(ZJS+1;#^L!oh{Eurh#tf%Avzm8q9gcAx?mW=BuMT_YFhhq)I*iw0 zlnz66Xr?#AAZH(P^VFeLheCx;|JLC}r+Thyba+aKl{!43!{Z?pyn9%O@9MBjhkKk} zBK}#YClD=GOr0?iPqJGf#WVW)8GYTT=gmfTaHVL}^J$~9kep0nRwd+=4k7OnGTTc1 zhto(>oYOTtr)#KeAu&45mpcAS9si{+F+}+cGDMdfqRS1@^$gMFhUjub zRC;!iRf!?G#1MUNdwp+vou<96m3mSnWqVx~c7}4Fmdf{d$nu^FRo$eYj{i!}DYxtE z+jaU?+J{-C{gYLybg@d;c9pK}DxKy{oqm-HWvuE88LRvs+0mXs7^GVfr0XA~-wo2O z2+}nS(lxwCw<1W_KUk*;*6#-EcR6!MKEe9kSlzl<3*HoEseXyoImhZeWA$CJx<_Kw zy#^Xc8f=tGsdGrM@Pu9_SU%Sw5|9a60%I&ExgKM|zav!dj#2Vq*FxPpMyF}eX&Q8z z2A!rsr{P2%X&Q8zNtRl!Cs{V?@E#qioG0nKCh5B-ai&kjsQlTxQ`b6&Njit_I!$*S z(_P0T>zHJnUd=U}3)AuOIzC>r8hZWn!>r{cxEsw_fUN~q32+^fnWM3RJ* zh4)rTxWx?Bz3i6hd(BYiwoUiWHZ7lRy2NcdK3BITSGOWpr^(fA$AUDK zSKoD;4kzj~3(adCuGTrM*72)#TbAp)R_k|XYB|ibY(j1`b-6QjxifW%Gxc3Fbq#0g za`)(aXPO~780=1|lX>dQVJBYI$-EHu9J_I)PUgk4zc+v@buzCHe)M#9bxpUm5* z&*Z(U&*c4s9WR0JF345jp$hU8Vj-yhY+^dsN(0p_IFh;#hJ4!5i`Q?6lkB~zaJ>%i z)T92a4z<57{-&?19Pvj<`Ix?bR)@QE_%FiNtCV@{qZxRjhVyW=&oHW#Z|bztGMiP1 z8nxH_*e|i~t33?+nQPu^{?q>Nrc3+x>dJnq>F<_T`$gg}=yyL*SN4Apo>e*9kK60* zpYhsY|IGeZ`wpgfHx;>?iDMMW*^D$X=taT7DdT z{8dwoz1n`j&a+wj4*PS>ajNgXYdesF9axB+xC1-TL!W%ee{#A(BBX<=_d`5SNHLs@9?5$-yQrE{j;C$Oqtun+D*>%SqsmK_ae-EwdJl%_2w z1`>FN)CDPcX(_bRQjnYs>B&xsl7$y1E>}{2)ox|2El9Hcxj{o_o_2T;F( z{5t8@w$ZJ1)~$8dt@Y6DG-&4r@=z`G(mjTsN~+TU)6qd$wP4F7jnewbDLCZ71^Z^%D8>E25>g@6`M#Exedo`lRGNQLL+p3eIvv>lvlUaBdzjM%%x6q~o z{4Up8vO;UgO7!C-BUhbmTFn#3Q|Ou+^=q`Qtkt@*j;BnQ#d@Awwh{Mfz1hn1Nq@18 zXOn^A0ZwIg6c6(JF;r}q5t5TaoRNwW5A(dSGta4%t$0N1(_@U14Dm-jO8(3VuYTe$ zdaUf?dDZQlAF@)85Erm*Kk(XU|CSRHf|8lI#YKCS_<@*4-n|U$eCkmB{Q{p!c}f^y z0Vi+}~&gg_{Cf-nf@ z-Ux_p$GJYUeKHT`#>)Ag*?cI ze$XEVz(5#8+QBe{>!G|3gW)g&M&dz?v+oq+VFKK2KOrW1lrouFs4h78O zGwiR6IrcZiT>D?dJeUs)U?CLQkBY_gQVA@9rLYW^!wOgltKfE64R^pASPSc5J#2uD za5rp%&9DXTfqUUT*b4Wbc`xaf-VPVmIE zojjG7gwyd(ZY1v{+WFt*KOI?CBFi(#vJzQVBFjo-S&1wwv2l{t8ejn@a0V;5fGfCx zJ9vO6c=1%FEvR4h*&H7ThX{y-C`iKlQqSf4KrZx!JjjQB&>sfCKo|le?cXqJ-eA}tY!v*F5KTd4=aaz-lHJ%^V-;dJ=ew;S&1={_+(pmdH zd_zC+B)xZ#-aAO|9i;aT(t8K#y@T}LL3-~Xy?2n_J4o*xr1uWuJNlq!wdimyI$Vnm z*P_FY`_2J2z4>K_5w;&xdAQQ468*-pK z^njkw3wl#VAIOEikO%qD5BkFZ7zl$%I~ay=J(SmBFdRm}VtWO4v;sR?fgP>Dj#gkt zE3l&#*wG5?Xa#n(0y|oP9j(BQR$xafu%i{&(F*Kn1$MLoJ6eGqt-y{}U`H#kqZQcE z3hZbFcC-RJT7ey{z>Zd+Ip1SPE3l&#*wG5?XoYwN`}Qn62Y-j>;RSdRUgG_iVK=-2 zufl7v2VRH0un*oK?VC`>^;^8|hqvJ!IKb-j5ak|*BXE?p*86Y_K7ix!A$$boPyrvq z3D(~yft4DXc?!)ug=U^YGx1`uBWKC$9Mr%!P|H1a@U6WD+gpLB?t_Q!!zxD-Yk&ou zz!|LI0|YmAQGY=3ESP9*FKO7eIXC>p&#^z0Wc7Tz&QJp z*p(--D^D^TUtl)Az-)Yh+4usp@dak%3(UqBn2j$m8(&~HzJUFCPuril_NTEuPh)$Y z#`Zjo?RgsQX+(P((Vj-MrxERGM0*<1o<_8%5$$P2dm7Q6Mzp69?P)}N8quCcw5Jj6 zX+(P((Vj-MrxERGM0*<1o<_8%5$$P2dm7Q6Mzp69?P)}F8qu6aG^Y{GX=J`gM?)IX zkVZ755e;cXLmJVLMl_@m4QWI}8qttOG^3GTX=ElGN8gQy2~dEAnE`W|1LnbeSO5zF zuZ7eF_SZ=9K7U1_4X2qi>##wuFmHatyjh10I*h+qE`zbx{`B-2dio4K&Dn8q1}nIL zE4YC>cz`D)!8qo!@h}1A^7lNL4+~%+cu?NY*t~yZ^Zre_7by1|%Ke7<`jTvGe_MJ( z3L9Lh%;_$qzTilGfmF=s_RBH_Um%t1w$#7r-S5cjOY%BJs*B|H9Vx$W&My=VPT_2B zD)h0}%T-9pO5XL<;u3e9BlUOWdz^fav-)hbpXJ^<>Qcvg`a5#IK)Q>Ji%UBF$E2?# zMKvj^x$hA7sm}p6;;po0#CW6k27lGdP+G>55ZYoDYNxA_^p2dlubTU+v5)^h^zT9B zbgd4)ggkFh|FA+Au|gNILKm?f7qK1}u^tz(9v4~R@EIOjuXJ<@t*c{n)iJv27+rOY zt~y3n9iyv`(N)Ljs$+E3F}ms)U3KD4a#_RcT383`VFPT0yI~V-hAnUp+za=?R=6Lw z!2|Fh`(E35eFz?g9q@D3b6_YP6N!*B#n@VD~kD&Y&Rzl5)#3aa50 zbDr86JqI=L4JY5*+TTFa-jMC=7o-n_+Rw;HKG6`3%OJE5sg=MfDR=`SF1-HX$xC7R}T383`VFPT0yI~V- zhAnUp+za=?R=6Lw!2_@z9)gEq2mFqHdx_VVVK=-2ufl7v2VRH0un)@M0D5u)Nt~qb zD{1`~Tz?5)K^0WPDXj;m(Sy_U{h3y+zJ$d&z+ERvRf&vL%20c)(hznC4)8hQO8aGE zmEK%nA1;OWQYmRZvZ_W_)yS$ESydyeYGhT7tg4Y!HL|KkR@KO=8d+5{Ue6$}YUEXo zysD8`HS(%PUe(B}8hKSCuWIB~jl8OnS2gmgMqbs(s~UM#Bd==H6X*3Ico=rTBk2C` zU?+RsyLkO8JONL_Q}8!<8lK_(XW=>cJ3J3Bz>Dw_`MeCf;T3olUV}aGI_!mg@CIq$ zgfgz*;&nf~4e!7K+SuCDKT4VJ!!h^(cwT|uRgd3QkKa{~-&K#_RZsub(gStKx`zI{ z-l{APC`u9m*5rSEF# zyIT6LmcFZ{?`rA0TKcY*zN@A0YU#UL`mUC~tEKNwqwlr!T`hfAOW)Nx`c3ss1AWs# z-!#xS4fIU|ebYeSG|)G-^i3^&Q%m2}(l@pAO)Y&>OW)MeH?{OlEq&8K-!#xSwe(E` zebYeSG|)E$1Pn+qB-h?L^>*_iUk4M@1lZ@v|d;wO0d{!Zu z&nNiMr^-@&EF(phjONn_F|3jdQvF11Eo0QY=X>sHV7E|xO7|o_Qzd?l`pb=-ZNc88 z;4^@N-ASQ$LeBE4o_DDyFzRXHy#LyZyWUFj+7)3E+I*v{HnmzCNBZ$F0SaISmQC%( z%;mj#Fdr7cLMTG&i|LgTSOQC787zktuo70m?XVi|fHkld*1>w%02|?M*aVwl3)}iKlfiqaa1zf=m+`$7pp$(SE zi`}=j;0^7-2YiY51AhpBKnQ~N5DXolBZPpOO*%msgmZ5ML_!oq6Bh$<5Dy8E2wk8n zbb}=7k<4ofq(T~`Lk46*7Gy&XbcY_$6M8{!%IE{R&=>L`ANoOm7ytud5NQX)5Uz*v zIt+%x2pEaP$0684-{+(n0NcYw}?wt<{U?CKd zW-(7!OJE5sg=MfDR=`SF1-HX$xC8Fwo;AF#g>|qVHo!)>8#cja*aG*!y>K6Fh5KO} zJOB@}uG`M*L+~){05!Az=NYvIt6hWDuEA>8V6|(o+BI118pg$^jEheh7oRdNK4n~d z%DDKHaq%hR;#0=Or;LkFvD!6Q?Hb0%r&#S8tac4ny9TRWg9g_yW)3rE4l`yBW3{W8 zQLC6ytC&%%m{F^kQLC6ytC&%%m{F^kQL7kR2hseaX#Qu6uY-)QgN(0(jIV=?uY-(+ z8phaR#@J!T*kQ)lVXSr)GintxY89j7h)iJIbHqbNfHRDM zGmL;UjDRzYfHP?F88rC}ntTRLR%_iy(6%#Z+ZnX&4BB=EZ99Xuok82qplxT+wliqk z8MN#ST6GGoI)_%BL#xiAEoU6sat3Yr<$B%JmNK-Z3~ebxTguRuGPI=(Z7D-r%FvcF zw51GfDMMSz(3CPXr3_6eLsQDolrl7>3{5FRQ_9ekGBl+OO({cD%FvWDG^GqpDMM4r z(3CPXr4mgkLsQDolrl7>jQM*%ny?>D*pDXcM-%p=3H#B6{b)iNnox!&l%WY_XhIp9 zP=+Ryp$TPZLK&J+h9>Ms6ZWGCd}@o={qQ!t0|)H8(S%Agp%P7~L=&)Ga11_xN6>^LXu=UR;Ru>=5KX8=6DrY!N;IJoO*o1s97Pk3 z;#|K*eT^k{!+t6aIgT|hLo?2>Q!MSfu%WxKp}Vl5yRf0Vu%WxKp}Vl5 zyRf0Vu%WxKp}Vl5yU?C0w5JN~sX}|I(4H!^rwZ+678vC4z0r0RAFnXur*cK zni_0PDcVzt_LQPMrD#tn+Ea@5l%hSQXiq8HQ;PPKqCKT(Pbu0{iuRPEJ*8+*DcVzt z_LQPMrD#tn+Ea@5l%hSQXiq8HQ;PPKqCKT(Pbu2-5!zFV_LQPMrD#tn+OrSs*@yP* zLwok2J^Rp}eQ3`H`@6zwTRdrHxsQnaTO?b(O+>_dA>(Vl&1 z&pxzgAKJ4I?cvlF789i1_kDb$V zc@fXB4%oL+`X4F%Zc6$C^EZAZ=O8+wd!fwXO7|9^dv)ku9lBSC?$x1tb?9Clx>twp z@oiGz4j$kMe4i7$V(K$8R-WUiPf}WWj$`FHj+N&)R(AcY)aVO5R4dPMtn3b0d5&Y{ zIgXX*I98tHSb2_PUg$Pv&|SZZZ3SC9njR!ZKJ6D_|w8g43%G(CxPu3Hg6Vag$7-C%YMjSvoX2XM{~vf==dm{D zu{P(iHs^29>#F@9cwOhQWaqJD=donxv1I45WaqJD=donxv1I45WanFXUFWakbycBd zA7kM@#=?D!*VTa6)qvO4fY;T4*VTa6)qvO4fY;T4*VTa6)qs|LftJ;vWnZCX$FPLQ zu!P63gvYRi$F$G9n6+F9EPtH=>fQ@iBY=X_O1@3`+ z;Xc?3_ro@L03Kx2Z0GeMco=rTOYk!6hF9QKcn$Wz>#!I0K^d6)4fR;HdaPPKR_zj2 z?Gje)5?1XJR_zj2?Gje)5?1XJR_zj2?GjpAhnD^eE&U3sR)?0>p`~?bX&qWxhpkah zA?vYf^;orftXe%*?Gje)5?1XJw&zf@ca*|fH5JmC8(Vuwy?NqjVXb1(Yf^t+)%QjQ zLJ+ivVCVqq`yN9e6goi|L_-Y3K|CZtB6NYS&<&Cy1yUgm(jfyfAq%o02f9NK=n1`G z5DbQ)Fbsyn2pGrwIUXi}>7zHIEr-ySLukt(wB-=matNDVi%qX(|Jsigc>t0)ho5+k zr(YKQ!*e_Vv+(>`a!%C%3pjx@SiuEc!42HO13aM(`)t182mZi1jTO{oR#2B&L0x7A zb(s~^WmZs^SwUT91$CJf)MZvsml?a4SutH^#dMh!(`8mnmsv4gX2o=w71L!_Oqa3f z_b_((9u!E0G)RXG$b>A&h8*Y)J)kG_f1T95gEgLr*!1MoVoEX z+JZN<10M*72#ACzNMcW^H?Msl7y3dTE`4+CHz3;}iKWgI6f#=``-nf{sxx4&dJ$WC5nFl@ zTT1I;HQWJfU@feJ^{@dp!rib5Hp3RU2kwRYU@P1Y+u#9s5bt9|8kEN)`Qq*H9>d}@1Xv+b# z#-v9Q9a^dMrmhmZKia zQIF-|TXP@>x3B8~Cnp%#g zmZPcVXlgl{T8^fcqp9U+YB`!(j;5BQspV*DIhtCIrk10rqyGwUB`*5^BI;3Fu93iudK zuoHNa6>lZpFItMlI4b*Z^9em4bL+u=L>WwwbMYOyUEq}Aw14!pA z0R4@ItG$tW_N0^8dmM=!V)quS_Z{=$NbF!G_o|abe8(W`90M%i1kPXu7jOkPa0d_Y z1TXfn+k!W=19jdz93nt{t4kClA(h^|_JLgJ3we+a{h&V#fPpXsRE@6QYf(GS4b-^? zZTSS7_6aua6KvWi*tAcW2Tn+Dh-UqjV86onzQXuE#rQr&%dc<__`f}o<;9HI7QCSy z_&_*BKqN##l6@ItsF5+$$QWv53^g)_8W}^4jG;!xP$OfgkulWB*lDCy4YaD3u~N%e zsnxA1B7QOPC9njR!ZKJ6D_|w8g4WJq`m>8h0G_{W;#CvpC>CH|x4_{p5VY>8jr96yWmpgP{3XnbtE z)f_)hs!x|Sm3x14d?B9!S=AiBqd8uEdpj$3RZq*yNY)vf(gy3~&)NnFhxJixZF}TQA2Dt0RjGgR+miuVvSVAJ^Na6W>B~A*Pe<`O^ttQ zPyZ=NgZf*CkIkGj#8!28QIEKeT{|p{k57m%N*$ftW4wQ2K;eL_TQXJA>QjT!_8*K# zjKkPi;T+M`NKfl!q^1M}x=?O}m*MXh%AXmTfmW;BI&4EhdV0b7;bYb$PYM~6m3&i= zu&^FCC3hbiGAVhTduG9gp+h%J&&o`i6c!cUdwg2@xZV*_;ggaxkf<(2>zDct6Qy6L zk*MK_SEa}OR{7|^)~nyBN2iqg#@(0l%9Q_5A4^5E=0&3!w4Rou3pX)4Vy>#H=+ zjK7ud^HTLyJ@g(u6v%4U?4gz}3PoC8hVGKTzMMZZJ7;Fzt9yoTEXd59zHylC+f6I7 zXAkH%D|E* zD?``Ya#+_p@;}vFI;4Ev7D#W0aIaU4<*+GL+QEGAMuEs|xnE4y37H63wn z7-p;6EaMg|u${bKNmxqN9`z~fR+cigU8G~&sj)Y3Q5r4QjU*1h+h_%kS$-Z4xLZL%2SC=H$YmqdUr}WG$Z#ui8$}*Yt zP%mZuPmRLeln-iTjG`FGvjBm8>{2d?-APanep=^CDJ-oZl`AX z={7c5E!8e1wZ*C}GHk^P8G~_hSc+OKMf7z>s@3Z67Z9MkI51k7KJ3MPL)I5$X3e;3 z`1l8tCWTM$o;NEeyP!|s$tgG6-j_2c{?R>i#`>YdNR!)rQbO16bNct6*CTmKzn+$* zzIXg>Y||*LwW!$#>qIZj;Im4qRnfL}1~qAppTj4sTGA_Nn(1%llZR@yQmx&5b!(O3 zH1$L#Z?gGI-ex{jE$*gTt3IhGj1QadVJ_BM%w0`)8XsO+w#jj`yfSyL&2m*1>bo6< zIIeTh@*TKIy|4Nli#vpP^=;dmRlE2GATQmnVE(dct&CCeQk__)y?EF{=37*I&G_l| zXLbBZ;`>@ud(HSsc2i68xcfw__*+@WD!I84f8L_B$4oyNf7{%d7P6;hz1DhRQTB&+ zM#*WmKhNJ}SwHjV(~g?AwI_;ttxf8FtDJXE>4h(T*#X+qPKdyW&M{0)S`WwqEvV=- zEo_ocYNt-!+HdYY&pkPQLU4!RlqmVRt;^sdKJBUd=1<(M{(l8uqKdncF13iivwa}r zp0>Rr$6a}P6JHxS(zd;~t*A+cXaIN7l4LEZIbuJrW^O)O<}NKGS7|h}?v>mXE0mTf zZSc`j)+dUYi?(a&=y;u{89&Y80}Z4s@}w*?eh#0ARbwcJe1SFIX!E8k zhm4M;SI(4fFuF6-JKD`RP0)^PEi?7q;OgX`&Xu{r%+>$ee4FiE>-_(${kd20=dLct zF$t*BkQ#MuE^UrFebihFZH%5J zyBd)Z$^eBL@_}I+3$pM;h7I3Pkd;-ienkF^?64lAQu`KWhj$8liE z-PvRAfHd5c?sNO6Pv{k)>Zi-oQtIuHk~$aJ6tBjh89&`&i_G+D44Uzi_|Daq^lGe` z@sq{ZTH0p%?{s_O*yC>HbzQ$r7M$ro_>H#Z5A3si8vIrpjcpM}a$bh!HmO;|tm7;+ zTiMG9`*((o?}}Ew34R%$Q;liy4Xp&$FdrX`!KMVWltT z&Fj@`UY@OPlXNZ$PL7O72`jxu>GVmZ?bC5d1hYi8#?k-ZM;sNTdvM>i~0Oj$N(JSyoo zc17~UPNTY~_m1j5U_ke%Ui0$@mPAkNlpEJA!;IPDo|7{%u1kD$SZv3@&@KZq`bMc4kfI@|jQF0_K<2@pJgBruJ;@jmB={B(qtBnqee6CoaASH{Icus0DQd z3urB1wU)M8QbLVn|D>)KZ7-OQ5x$(bd+Eauk4n!C@Eej4(>*+*XQEH6SC63NfQ<1e zeV46Gnlh|wOrL>yL7Btj*N@yH|NM6N&LOtHdbj;kXjDjgWOQnXv-40_kJMZH^qbq= zsPb*^H^Ifz!zC^+)|Mn|2F>e^tobGwg9#KX>+n^SZTexe?GgUx;tN+s8vSus)RR^xuJQ58-ScB1^JEBJw1o>2uk;QqkC)uVW(6-cTQ)l zHF@yr694_y(Q5~9is=`h&^KoD;5*$1t+`1)ZyVMvKRPo3?b4E7&7)@gWWKXQ%~57P-)TF` zcfMS|4b5}s)onTAm@BW_CX1J2f>c^&mZD}zv-DYf3fZhxhLT=${5-xl-n}_}Lvwr~ zpBh%*tEG0ho%u1!pWwFBMu;ZY=y>G}lDZi`Ny=97O6q3(WWH^pY2WX8-44A!8E(OYr7hty(Bd28n`(F;_OsPLe#lR@ zyHY4!W_;K<L_EZVXsHrM;@1zFXI>$Wgm(*W};PVKc8Tr)6KkEKRk| zEbUf4>8{$)kuf#0lm7PW&E5D=K28Iz9Wo%+u0(PM7k zv{A{1kvytnW`3v4=%~z2oiZQw_h0AUw|Kz7;=b`iy8S(AaKef~Ii0(Qhj;IcaV%Q5 zv}BnrM73U*Yy3og7CTzMRapP=q zy7{eo8}&`QG>-Z-*K+#6mNu!euG(aklX6;Oe`s#$WWIx-CH~3g)U*1Uv57{KJl~vp zp2V+il9$D=IlfS=X^A)XHs>-k&5_!&N%9m|Y?kX)U386Di6X}&qZ~N~s+GNxVobW; z$JG;DG^5HT&_A>^-kqRqYaW*G$p#xSvH%`n%`O)I5<0831CpbQzfIKSuY zUNK3XJX1b?>_Beb;(-yW3K0=mVPTou!{q#M_r8nz<<08u6A|bo<85ar-Q;=~tzI0jbvAy<$7>&eH) zbny4z5RuVo*|IAqn@9ey%1P_5VMTxSu4YU5>LH7BdbCT^-h%cXwHk*HUD_|NWI+FU zJuObQx$@?8qeE(ROiripUUAVq0)|Bvy5|-T8hBe??*;wZbZE!_QHx8+=@i*JAu%_y zefwc?4y`tiMC^m3{opAX|{ASUR_IiWhKpAW~G?18LBUuQ_tf(mHJ<^ z?l`O!#+QsE{HRprpSM~Z>Bns=QzrH5mfvBtN3dU1%=dDg5twsxT3E+19-c8#SI){s zi!t4tLbS&l?#zn5=$zg+XkK%f6Chr1%x~zRbQ-oaa&*Uj$vp=I_^-=bIy!fD_wKWE zGe##dWsdF;l^zbLZY^%NRJX$LZb+^YiETjvti#Xws0l zWt}o3qcRv)krA0;+J;a!L(P!ewY1F{QrFnh(^2CA+O|7(SkrH~&b}@#85w~A0WLJJ zb7xjQnVC1QQ=)IvaRp9|E6We{i|KucuS*OctkggB_qicO#csuq3)MORoFbe+W=KGx)2^|yLrzVZf%p99!%UUn} z6EcDlLxQu%CMAu@v>h|buc))5Dnpf*T0^n3uJ-SJeDv@OI)P6+FinAj;Sfw__$WAP^6v`XFB*=U!u zUZp@9Zp5G0_TcwiS>=>!Mgym#giO`r&y1b^n2J3}EKk0WoEbamJ{9{T_f_g~X2woF zPAsRm;^iUg*_m&%L&}UMmCoQ?kdTS|-#@+bY})TnSNr`%>y0O8UU|!`m;Gn<`#o3>Ze(YoWS}O?h$zg5G9kg$kl8>Cj_dg*Tv!Ar)IQmK5K>7&Yv})h}37vk5 z{`4*R5Wk)Do%!FVdY0q+CDjTe*tSlb(D|!)tDJ-QwT{aXI(JrsO-~JSq`$3h(0V!c z@uiP{rRzuUkgp{bd!;lu&eR#K-?d|+CCcIQp;&v;V}j#@Qj^D|r;JMCkM;>cL!2@u zWMqs>QGF{z;=B3Bc5I(Ds!NxfvTcVQfBWQ&?bc;Xj_p{}NTlo=8Hvf`Gtsl!vTg6-DOk4ovOUJ9!XketnXKS{%scN?<=9e9ym26mR?UbfD zX|8FLwjO*JjX%-KtTfjbiL1G?6P`$0bqb*MN=O%bt@9o-Xi2~Pr2_{q$1&^_^zqbFELG{>u%SH&~e z_iTw(w#JN|)U_p6&FN+ylQ|uuR&zRUi@&aalkA%Px$Cu|d3Wa5+HzCVKFzPTN$zQR znqjt0`4nbtXJxAXY->MlBpT|?F+9y^HOF7CUgHPKj|bWs2C`gf5^zk9@CepD zJ;K9#gy?sBwXRZeWbfFxo{^C~<6?V9HcL}!mRXaTO+J$4KKj=oH`Z`>BR8dS8ES2a z#$9*y#?HRKOJFr>sv-4vM>ILf1wUwpbYnjR51&k78l?CF`VtO_=t{|rSR zzokdalzi2fopo$ISMBuKuwUfcWX!I~{|Se8eswu%^4XS^r){02zRl&SxkPea{F2-y z|D|S!K(*>OAHMSl!O5|ZG7oa8M+i=+dR5$rzCpv=_l*q;kId?s71?D>c8_UcBLXtR zLOKzV89CD3q0pnk^Xru`f*R_jOynVGemJ3!YRNw&nQS$`UB zVAjX}CL5JkuMAoaVL8rYMOgjKM+A6#%H4CxG#`k{VTqZ6Iqn?-gX7zWq;yIPYainu z7}qf&V_o*Fe)-eVduAAEL*pWPj&b+%EpYV+>e?wZDP&<_LP&?Wpwz@Gmkp;L({nO! z$>=`LH{LyKOlFtiIk+2!@`U6P<5@HcZ$as)I;XRC{Y6=5JbPt~UfIweD%BB7Ci-`X zwy@6TKcbFN@wAn@k${TF1L>_=+J)VdQ+Qddrmo&McldD9wNF9T2EW)2ab4GE7Px0j z?Ja|Bb)g9XDP3?=YWhq~S8~+3XdO7J%bcSAb3t>wTFIz*_WEL5V$}>|#!hb)t7aH8 zc2aOlS~bI%v6F9AV@sFE{Nl%FZ16m2ST47%43?h}j}FgP7&c ziZpAJp`_)Ab&>PPs~NAPY{pOLG(t38$KPbV<# z@!un`?N&z~BG4fVb&UC=!&~o4y666rr?-!s;3Uu4?v*9B_6KF=Z3n55zDu|43rEY$ z9Ro97wM@n1p~R^Ao3W~0X6&S(mRQv)Gj=lnK0|#LQOH-R&vp0pgQWPAUHoabOtP(% z_s$t2huK~pG9;#0-c~$Bm!$93^*W`?pF*BaW}fWW{K~F=lYP4;_ioyl?VfUVDROm_ z-7?Ektuo8Hl^M&Tc0mj~yCCY^ac5-R#U*@i zJZGzKy;EWsJtt>o+??SW->qxxl^>Kfv28N~vnRIfov2p2;}fSk_FtlvZEq^OX?D>? zvj}dQe=t+H%B`1zE4z-)j>--m;^yxa9P+*V>bBPIa-U9dah<|qV~x&fqmu*tC%L$E z>Tu;_Rhuh&uU;)0Rq>rV#Vd5ZkVsR#g4omfm3k?o*=kjKZB=@&@B6Fe+qS70*>GDo{UZSUY2LAH@q_^XRk<#xN_bhU9~yEiXU&) ztv|UaZ$FLCu=e2dsW1HKh z*Z7doxVX@e_$xoEX${FDAFsz+$!AcrdFR%PFqbOwBlG`g*Saf%DVvu{ zY_nUGcWV5_;lrxWBq6FMQ;8>QY`eI=$(LpJQNy2my!g-Q{f&PpOQ243GnX{&B?S8a zuhy;vIIim0PQS4GeaNdY7}oQ@(7`qMA~Z!)Y`*9BT^-hUt_i zRbHO*gq%kl;ni-l#pO1e-Mu|+ZSBJEnVynQC#eUJ3PnG(SvWH%WXvl~1oU)8htJm@ z2(2*VWl`miHa&?K!%t z|7cIok^W#S&$k6Dac#{RdSY_&@u8tJJ15T!9*S-6?cE-YZtGpKJ;q?@q0{i9uY$Zg zU^48nW5RocGa5LwckJJfQU(;w1|}u!lr~Ovi1HDgprjqI*v~?axi}bTiOI1vQ4we> z8}{9>vSqwlTjl@3Q`gdg9@1JW{krl{Fw)!B5gTdnx3>6y)YIeHkFnCAryQ^_!$Qhu zT5e|~;2d|WXgscBF#@WO+V*>fG@X7AEL3%VHT4K10v4WpEOXr0KHp7a5^IdETb|xZ?nj?y6w+o}|-V zYg(-KMV;N_vD9@xZL}V8HfiZr32Vq&K4wyD*Jw)f>$lanOw>|K1Eqv#UcsPXR5DL! zWl1LDJ_~fdI3rX0XPO(HKmF@aZdu4(zojm9ZJQ%zwZ#HjXpVnNwP3JyY>)11z%S4B^CMe;BONoI|><$y#??Q0b{Q#W&U0kHk%J zud=Guy=-TvEn+rDZ1%9l61MZ95YLChIgt%DRV(7TwwzFlE3qaRT$6CMgmP@T@fB4y z8zNK2prx$RXspB*+DM5I??E+Bi1iNhn==4scIu7Z#SxD+hoM!<`F6BJzp2VA3-Wd2 zjF=#AcZt0zIrVkW?Uci zf$IY?iR;T-Q)#ZZqu(arBy&C1+6DZR;bfWSddlyd@(t>`MGk7LPlKli`)hUoo^SKxcbcUIJs`Tk0H&$3{e?{o;Bod*jm@JuN|%yU}{IgYE4`f-@`)4Ib>?Fs9+_VJF3&f9sPJ( zK_AcId{{O(+y4{iySsMR@f|_Vo3JEC>l?Z&-OExF!*iSv)X8p^X)@%7snIXm{Hnl7ls6BLQ4^EiLQhAVJBvYv9s zxtB2)vHHRVk|$oRCsX8iBeaWM;!)wf5lTj=Xats-%+k!N;$nW^=~|*r6xOP7JU?f3 zhG=DLb687;$Zw@~1={iS*@Gu?7i)@_8jD)>9sc_EQq#cAgWsh{h(TNy0z~0$x<>c{ z(1rYW-%S8q{V#rjA82P;V(|-Z63uL|WB8HZiS2)9DUp^VG94sW zZP0-S8B}mo)T-3@FpE!4<$;2%qduJ2pM$Q$ zq!!bLXG|@UCU*rI-T!H{puSJ;HPeks(Tfb7Y7V_iXX*IM*`X!mtM~vhPN!Mc0q9a` zCxH8%fq>Hy2;>AEjv#wssF6eF8dxk(Gy91?9?#CkDxI#NQYG5Fs^YxT@}Sce%3In! zbtE^xxZLIpg`5Sv1rDV+LkFza=taPS2Al~k)6jD#gPifo3EG{(pwk|tK+xf=taLJ5 zIOMmvM)5k@V!wHGC2TUySkYE*c5$Aeyvkt<J zH_%s}3Ki3^9paq?(S``?FW^Vemsdr90DO4pfXO zE{bPZsHukyIkWdInlVkQT=PZxw$RQMrLh83_$JcJ*j*c$#vbRzkWD!@B4b;iimNiQ z`NblK*i@xFL&)#yV(-oR@ryuG#UoGIN~jDdp8iYMrKSjr2h&yfc+utqVB-&_gWgDMeIuzYF@>|8qSh)AUB8pcgh>XCl2(k z3ylq&F228l>K+~#4X^7xaBuh~<*vX3u%j}K>>62uNOpDT-)t+CCF|kTw0B~UaDX!l zHhee^^$nZ2mo{&HiM`Xqn>RBIvXPzaF!yJ0DMS*yMMC%`-x@AQ?QI6Ng8@#Y6@*86+fZB6U(S4-~ZlECXsCv_g_@nh-KW9?sI>7 zaXYf#;OtjCD4)wKX0J z-f?DN@XQ^-RNZq^`=6_0H8rT2FW1BbgzV?VleO&Z;f}`E*2a!_kKf;O2sQRkJy(}P z3xfk^?qD@_(~nXB`~&J@KE>lPIb#OTyJO+77Sl?v*I}O$wQwp`whp)6@9$rntJIkA zr%~oE?(a|feJ16SMTJ#?Kvm(QC7jXc9~oGCgU!6zVm-KKaMbDE=1aQs+&QkKZ-RG> z0je2LV=`1RK?egDi)#a18UvjSPR3xMMueg)3-{58NNPpM^?Xn{%J$e4ql z@FiV20G9M^b2~=|*BrE3Hk)lXtQ`QgTL3j7$_jBGAjKPpwMs0CEfeu`=gy5N!Rf|Ah~P$ub25tJ-QZ_J6yLdTOY(h2dOGKR1xFCKEy|iJsfD*%eOOawk$C2CUO|yRy{Ne6er59tJypCS_Vh~qoY$%O zoj`qH4evRIHS~Z5Yh2*d@nI5Lv7CNM4*4Y78z(0E#Ov}W3W+dCtRX{?+7^p28n6}>@^i5z<7^WQVuGGU zdJX1d37v?rcG>VEt>LIac-L^uh^M8(pV$j+RnxPI?~7#>YLx0T&OFC6wW3*RgEsn-X{7}zW|||rSMEY#Ql(qBmOsztY2-&=?UveI&+P9 zCeMo|F3C|&ifUtGD8si`%hbsX1BD(8#JulW6wGyPht`K98*XUdc;Bib&9*{yPIRob zeR5fHYfG@Fthr)Ozq`@nX>FI83X%qej^kh4)$FPgh zBl9UD#nLuUIU$cINlDr>imRlYbtA4X;J9qz+b(Bxcv)pilu!6RFyDFjs5p%%MZ?<_XCcaeY%c#IHT&Q=DT)%zUT0!NMz&T-rgf4k;us5RLw|r^`>PN-R7j&yH#atpA^pPf^4_k>mE*Bhm6fYv(N%%Ks@&+(;^L*z1U#f|lwY%@u6`_8 zGgeo>CAs^JH%66Py5O;jGvKPeNVLD|EB2y-{9^Ek1xLF$zo&Zco7^L!tbx`q?ZB8% zH&n+s%ex__F4ErK@=bDSgC|V)1XBfLm7@hz&msrOQCllqq>(q@6drv?`RJp{ci7ys z(QUL%>^qA=pWo{Y0r8_?r_a|Jl&%K5-GCR}=5MbEbol)pfr@tjW>=}n=`@vMStR7; zyHW9TWfpK%ur+$k)UC?fE?vhU&OU*x6Z;@OpPHgO={n)I%YUVV!ZDO9p(HY#JOLX4 zJk2uqd!G;;j`OEEWO&fOth8dO*KD*FTbvF{u{CCQCW|WyZCbst*z9zgi(AGsiIg;( zE3`UYaiKOpuTbwaSSw15+KQs3#YF|`{M;hF%T!gJHHUm0BybMB&3zMc0j6cwEcNta z_OQ8g9K9re2Xm?HtR8W$R!6KZX1j9PBI?!7k-Zzo#52JKF(8u-j!`xWz47R4QcPg*llz^P8-3)&Eb80lzRm$she5rW7FxosS z+&r2Z{ay2D^U2t7GPy1mU7t)2$6U>0DYoq}dUUuNJo*PmQ)3^acr*SnRzAo`i_(d) zg4CY1Cw^iiKRcNqOYgqmKv{1zR_Cl~ZLM+C?npK5b`4tUomFx){~h;iL_GhXx2oJT zC!T-M?!*~ZI-Y+>dHbaUm}xnXL1(dclu73q8EfJd#sk(roMYQm^i=oF8^WOtH+QeN z4q4rLV|5!Kahf8%$mw>yP)M&Eg(N*|$jqi@ch>A^YumBRWk+r|x_fx|$@QXtO%0}Z z6S9Ud{}i0b%sXOdZgXs>fY;}iZEI>9F|XAIcymR`oAd!fX+KAAwQWr#d~3_Br2(6u zMcOIH_&B5!C@SVeWcscop%lY8+cddsnR!9$rP8 zC)r2g0ouGuc$i7Juwo6$vz+D9StRRMhAkj-OAIJ~kiAx{@;ih2h`+4N&tAe~xH3l> zKkx`E9s!1zBs>_77LcTrxUrckSGD=){p zMu&Gs{jlO`^!mBkUh~lL(Cce4d|i9?tosPP;pqaMfli7MCO)h}q`jJIc13n?JGJT* zU3lpw$`jsaZLOJZ;BrOZF`POui4L1VG_GMQxa>2z=E$DaYipu*XXr?1&3W$3<>7Bl z^y@?AeHFbTCLG2yv{ef-Ax{Evl}<{2BS`EFfb!R4lCKf^9^2aBfrt4!`4%) zGf^Lpvf^zthTA$5d$FFkE6TV|?kI3x0vcfiU4#r>L3sG(Ho(Cscl)NSJ<- zUBp@|k>jjSuEp{PDP3_}5k1o{+_#$UCgIdRn&8y<)*qtnv`@I7!O|&Ka@VJ8X%QUE z@_|9~&upFUo|+P#XFwVB`LJ9niYr}ehtHou%Z9Y`6E*uGMnmbiPkGDbey;fPzhw^U@R0)J2>}W;;*Fac#48P}aklz_%Y@q+t(>>;LON zR^P*!uHAB)X+a*&hG+knc{UU}(a+iHPH$m7^l(Mk(+haUq^rC>@0J5TxT2XH9B8E% zzFwEWd)P-~7aX5wEIyfN}1g@qo3Wl4gAJu$Bzk|T#W3v;==8CzW7lP)!r z1CzGQ6#I+hhR;gr(bmsIe`}n1i<_aaC9r$+9{00QUohAgvX*Nc zTCu-+@+vj5WS`ZQ7I6Gm+)2AV>2f9P_Jp&zw6s`TTFU9|IKYRS1cIxRwv`5r+F($t zp|VOeKF>XiHCMr8MOtdk1}xXw0v>%ySwWMz*2_MYmgVCkXDiVatDD>&xb+q{x^XadH!royeh8|45}M}P&87P^@KH1q5)gpZhH{ZH=+u@PZML@`Fx z+uhZs%DghIUaQsnY<;D^hOW+E6&KYk$;)4oTe!8|Mp3T^`N?5VC?^%{(Q46th+_!Z zWGNe>pNw|aCc50^YIVMDaamE|SHWmyo7zwmuoP*QENabxQ6JGDGVdWfglIRL;UHwu z5x>uHNj7beze}8f$O-Wb;3~NRkY?s7JfKmNjoCk)w_+`6-ohJShA+*=Y`P|m51U1Z zf|I{XGa;KL6F~hLdL!e3^jxMHVnRhM9Q1FBH(?dOvaQRpwnDVMRk|F(0`+?O>5*K? z_7-#&dd+vEeMSUijD^Ru8bg{*mJ6FO3p4i@P_;gNhrw2NHTI#Oy35#zzfag{)STj$ hJkGSROl>}NRr-7y^i}Ef42P@H>+2rm-ua?J@qeB#pD+Lb literal 0 HcmV?d00001 diff --git a/pages/proactive-chat/fonts/outfit-700.ttf b/pages/proactive-chat/fonts/outfit-700.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0389ff27183e883930b4d83a18de380bf699ac03 GIT binary patch literal 48340 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 J^8tML{|`q-nqL3_ literal 0 HcmV?d00001 diff --git a/pages/proactive-chat/fonts/outfit-800.ttf b/pages/proactive-chat/fonts/outfit-800.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6f0672bdeec68d36ceb605c95a065619320b7b3d GIT binary patch literal 48272 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 z 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

Ic zk_CwnWU<^Q`cO>YQZJ$z2RhH(Ek1VTOBCYl6YO-t7=-6%&X8kdAAdLE6z=5rqnwI_ zm>)6UfPMgS0(v&P?r$6w4(rn#G@Px7m4;+aEvJ&{&E-0oDQvaI*-(jzl`0c=7L8{j zDO9Sfl`BdL3#0`FB`S-itV*L%h9I885v(9zsqgc5RGhz$2Ww-8$Rrt=nKkq5M<#j63Bg zzqJ=}{F7Fvp>%E>zsac9%*OHei%(qHioBC}S0(c#P7XuLmXSUYZ!jOw_`{BEl8{Hb zriX)p;d{Dzrjf<1z1p|553U>jWG!9CTmJ*r0=pH4qBz=h}BE_89PbCmAMl4Z$aPiS~kG}Y) zhraWQ`cv2a2O#-H&G#Oo*D0V~`%Ob#}vZNrRQ2bedKS>fY zaQF=XUpX`L7yg;2fG#59$O3qqy+-GjfRh8;mM~1})7b{UR90y&Q)wLRDtnL5P*HB- zv@W*Kv79WBvQmy)<*kt_rL3mchi!q5`kR;+*XZ8xnava4{v;!y!_T%gya86TkE_QVc-Ca)?itp`#CfP{-A<#l}Ufe z&hs04SGC$yD(lm}-dC8XulBq+(aTzmMl0I56kb__b&Pi?LQ_oN0V@ZMR*9f^qPaMe zyd0I7=AAn8&fyG(wTYZQ&5shhv)OK6=ycJmtAu4sN)#;rk|TA~vCt(|uqDL`1yvp7 z*_)ltCcC}KiCWtfC5iGRl6iX)P0(goI`Re)$1G!xqtCFk$bmx(K}Ife?bIoD<*1VmNTmpwZO>Rvc#64NlG3Z-NZk! zX@J}zKFJ@VwTe*dK)M!d^2=%dFG7Bj**tmp)G1iH0G5KE2TMYNVv8J^uowi0KCx+# zY^SZ01!JUljNh|>Y)`^!5GgUPV~HMGz`JG<0<$i;KeP3>$@k9CA-+j3^TtJK+9J=A zn53U?BCE#vJz}!`1b^sjn@I1bLH?*1L!@;IF{ma6WwhqeL;o<2N3;wKfoUniU#qy^a-ReWf zDSg=)6>&0GfMw_wgIvGo{4z$h^0+YDc7|cH#aVbp_od|dyek>(!7!=Z8)&6NbPrL0 z`XGfv2EJa5ezbdzqWGS&a{jEyXicA3 zo7~-Jd8K(Yf8JJSHrH9L^lFi*R5FE1#mLGd7HiaOj?!zdMlM%t=HOJpz}*}#}GxRfk+TW z9&*;|Yl`$G@?w@#n)*vCo7+0ylSvzM^YU|Z3&t1O{BCDZ7&qFgg&tZiw1xGaR9Plf z(66_9vY@xHi0a8w)Q4zjL2s6#p=?-;=<^hv zWRt<)EhScJHUSfJYb#f%l~}p(#!tdetjBBur*+|ZVjUp{F8MpL z7P83(;dct($aj!aWwH;)%mbB?nv;+EE! zPXj(h0&>K{=UEODOg7P9T93UUQ^J6@@rz{)8#Fj_NTOdTXY9qlo5&hnk2B19%8O<4 c^PXGc=+mHYiL0l5xE0QR;JeHRPl`nU3)dO7MgRZ+ literal 0 HcmV?d00001 diff --git a/pages/proactive-chat/fonts/outfit.css b/pages/proactive-chat/fonts/outfit.css new file mode 100644 index 0000000..d997f61 --- /dev/null +++ b/pages/proactive-chat/fonts/outfit.css @@ -0,0 +1,45 @@ +/* + * Outfit 字体族定义。 + * 采用本地静态字体文件而非远程 CDN,确保插件离线可用且首屏加载更稳定。 + * 各个字重拆分声明,浏览器可根据 font-weight 自动匹配最接近的字体文件。 + */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 400; + /* swap 可避免字体尚未加载时页面长时间不可见。 */ + font-display: swap; + src: url(outfit-400.ttf) format('truetype'); +} + +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(outfit-500.ttf) format('truetype'); +} + +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(outfit-600.ttf) format('truetype'); +} + +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(outfit-700.ttf) format('truetype'); +} + +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(outfit-800.ttf) format('truetype'); +} diff --git a/pages/proactive-chat/index.html b/pages/proactive-chat/index.html index e105faa..f5154e2 100644 --- a/pages/proactive-chat/index.html +++ b/pages/proactive-chat/index.html @@ -2,219 +2,28 @@ - + 主动消息管理端 - + + + + + -

'); + $("view-notifications").innerHTML = html.join(""); + } + + function renderDocs() { + var files = state.markdownFiles || []; + var doc = state.markdownDocument || {}; + var options = []; + for (var i = 0; i < files.length; i += 1) { + var file = files[i] || {}; + var selected = file.path === state.selectedMarkdownPath ? " selected" : ""; + options.push(''); + } + $("view-docs").innerHTML = [ + '
', + '
文档目录
选择要浏览的 Markdown 文档
', + '', + '
', + '
', + doc.content ? renderMarkdown(doc.content) : '
请选择或刷新文档
', + '
', + '
' + ].join(""); + } + + function renderMarkdown(markdown) { + var source = String(markdown || "").replace(/\r\n/g, "\n"); + var blocks = source.split(/\n{2,}/); + var out = []; + + function inline(value) { + return escapeHtml(value) + .replace(/`([^`]+)`/g, "$1") + .replace(/\*\*([^*]+)\*\*/g, "$1") + .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '$1'); + } + + function renderTable(block) { + var rows = block.split("\n"); + if (rows.length < 2 || rows[0].indexOf("|") < 0 || !/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(rows[1])) return ""; + var headers = rows[0].split("|").map(function (cell) { return cell.trim(); }).filter(Boolean); + var body = rows.slice(2); + var html = ['
']; + for (var h = 0; h < headers.length; h += 1) html.push(""); + html.push(""); + for (var r = 0; r < body.length; r += 1) { + if (!body[r].trim()) continue; + var cells = body[r].split("|").map(function (cell) { return cell.trim(); }).filter(function (cell, idx, arr) { + return !(idx === 0 && cell === "") && !(idx === arr.length - 1 && cell === ""); + }); + html.push(""); + for (var c = 0; c < headers.length; c += 1) html.push(""); + html.push(""); + } + html.push("
", inline(headers[h]), "
", inline(cells[c] || ""), "
"); + return html.join(""); + } + + for (var i = 0; i < blocks.length; i += 1) { + var block = blocks[i]; + var table = renderTable(block); + if (table) { + out.push(table); + continue; + } + var fence = block.match(/^```([\w-]*)\n([\s\S]*?)```$/); + if (fence) { + out.push('
' + escapeHtml(fence[2]) + "
"); + continue; + } + if (/^###\s+/.test(block)) { + out.push("

" + inline(block.replace(/^###\s+/, "")) + "

"); + continue; + } + if (/^##\s+/.test(block)) { + out.push("

" + inline(block.replace(/^##\s+/, "")) + "

"); + continue; + } + if (/^#\s+/.test(block)) { + out.push("

" + inline(block.replace(/^#\s+/, "")) + "

"); + continue; + } + if (/^\s*[-*]\s+/m.test(block)) { + var items = block.split("\n").filter(Boolean); + out.push("
    " + items.map(function (line) { return "
  • " + inline(line.replace(/^\s*[-*]\s+/, "")) + "
  • "; }).join("") + "
"); + continue; + } + if (/^\s*>\s+/m.test(block)) { + out.push("
" + inline(block.replace(/^\s*>\s?/gm, "")).replace(/\n/g, "
") + "
"); + continue; + } + out.push("

" + inline(block).replace(/\n/g, "
") + "

"); + } + return out.join(""); + } + + function detectSessionType(sessionId) { + var raw = String(sessionId || ""); + if (raw.indexOf(":GroupMessage:") >= 0 || raw.indexOf(":GuildMessage:") >= 0) return "group"; + return "friend"; + } + + function getSessionSchemaEntries(schema, sessionType) { + var rootKey = sessionType === "group" ? "group_settings" : "friend_settings"; + var rootItems = schema && schema[rootKey] && schema[rootKey].items || {}; + var orderedKeys = [ + "auto_trigger_settings", + "group_idle_trigger_minutes", + "proactive_prompt", + "context_settings", + "schedule_settings", + "tts_settings", + "segmented_reply_settings" + ]; + var entries = []; + entries.push(["session_name", rootItems.session_name || { + type: "string", + default: "", + description: "会话备注名", + hint: "用于日志和管理端展示。为空时将回退显示 UMO。" + }]); + for (var i = 0; i < orderedKeys.length; i += 1) { + if (rootItems[orderedKeys[i]]) entries.push([orderedKeys[i], rootItems[orderedKeys[i]]]); + } + return entries; + } + + function getConfigEntries() { + var schema = state.configSchema || {}; + if (state.configMode === "session") { + return getSessionSchemaEntries(schema, detectSessionType(state.selectedSession)); + } + var keys = objectKeys(schema); + var entries = []; + for (var i = 0; i < keys.length; i += 1) entries.push([keys[i], schema[keys[i]]]); + return entries; + } + + function getAllExpandablePathsFromEntries(entries, prefix) { + var paths = []; + for (var i = 0; i < entries.length; i += 1) { + var key = entries[i][0]; + var schema = entries[i][1] || {}; + var path = prefix ? prefix + "." + key : key; + if (schema.type === "object" && schema.items) { + paths.push(path); + var childEntries = []; + var childKeys = objectKeys(schema.items); + for (var j = 0; j < childKeys.length; j += 1) childEntries.push([childKeys[j], schema.items[childKeys[j]]]); + paths = paths.concat(getAllExpandablePathsFromEntries(childEntries, path)); + } + } + return paths; + } + + function schemaTitle(key, schema) { + return stripLeadingEmoji(schema && schema.description || key); + } + + function schemaDefault(schema) { + if (!schema) return ""; + if (schema.default !== undefined) return schema.default; + if (schema.type === "bool" || schema.type === "boolean") return false; + if (schema.type === "int" || schema.type === "integer" || schema.type === "number" || schema.type === "float" || schema.type === "double") return 0; + if (schema.type === "list" || schema.type === "array") return []; + if (schema.type === "object") return {}; + return ""; + } + + function fieldValue(path, schema) { + var value = getByPath(state.config, path); + return value === undefined ? schemaDefault(schema) : value; + } + + function conditionMatches(path, schema) { + if (!schema || !schema.condition) return true; + var parts = parsePath(path); + parts.pop(); + var parent = getByPath(state.config, parts.join(".")) || {}; + var keys = objectKeys(schema.condition); + for (var i = 0; i < keys.length; i += 1) { + if (parent[keys[i]] !== schema.condition[keys[i]]) return false; + } + return true; + } + + function controlAttrs(path, type) { + return ' data-config-path="' + escapeHtml(path) + '" data-config-type="' + escapeHtml(type || "string") + '"'; + } + + function renderConfigField(key, schema, path, depth) { + schema = schema || {}; + if (schema.hidden || !conditionMatches(path, schema)) return ""; + var value = fieldValue(path, schema); + var title = schemaTitle(key, schema); + var hint = schema.hint ? '
' + escapeHtml(schema.hint) + '
' : ""; + var type = schema.type || "string"; + + if (type === "object" && schema.items) { + var expanded = state.expandedKeys.indexOf(path) >= 0; + var childKeys = objectKeys(schema.items); + var children = []; + if (expanded) { + for (var i = 0; i < childKeys.length; i += 1) { + var childKey = childKeys[i]; + children.push(renderConfigField(childKey, schema.items[childKey], path + "." + childKey, depth + 1)); + } + } + return [ + '
', + '', + expanded ? '
' + children.join("") + '
' : "", + '
' + ].join(""); + } + + var input = ""; + if (type === "bool" || type === "boolean") { + input = ''; + } else if (type === "int" || type === "integer" || type === "number" || type === "float" || type === "double") { + var slider = schema.slider || {}; + var min = slider.min !== undefined ? slider.min : schema.minimum; + var max = slider.max !== undefined ? slider.max : schema.maximum; + var step = slider.step !== undefined ? slider.step : (type === "int" || type === "integer" ? 1 : 0.1); + var rangeAttrs = ""; + if (min !== undefined) rangeAttrs += ' min="' + escapeHtml(min) + '"'; + if (max !== undefined) rangeAttrs += ' max="' + escapeHtml(max) + '"'; + if (step !== undefined) rangeAttrs += ' step="' + escapeHtml(step) + '"'; + input = [ + '
', + min !== undefined && max !== undefined ? '' : "", + '', + '
' + ].join(""); + } else if ((schema.options && Array.isArray(schema.options))) { + var labels = Array.isArray(schema.labels) ? schema.labels : []; + var options = []; + for (var o = 0; o < schema.options.length; o += 1) { + options.push(''); + } + input = ''; + } else if (type === "list" || type === "array") { + input = ''; + } else if (type === "text") { + input = ''; + } else { + input = ''; + } + + return [ + '
', + '
', escapeHtml(title), '
', hint, '
', + '
', input, '
', + '
' + ].join(""); + } + + function renderConfig() { + var schema = state.configSchema || {}; + var entries = getConfigEntries(); + var sessionOptions = ['']; + for (var i = 0; i < state.sessions.length; i += 1) { + var s = state.sessions[i] || {}; + var value = s.session || s; + var display = s.session_display_name || s.session_name || value; + if (s.session_name && s.session_name !== display) display += " (" + value + ")"; + sessionOptions.push(''); + } + var fields = []; + for (var e = 0; e < entries.length; e += 1) { + fields.push(renderConfigField(entries[e][0], entries[e][1], entries[e][0], 0)); } - return webAdmin.url || DEFAULT_URL; + var selectedMeta = null; + for (var m = 0; m < state.sessions.length; m += 1) { + if ((state.sessions[m].session || state.sessions[m]) === state.selectedSession) selectedMeta = state.sessions[m]; + } + var canSaveSession = state.configMode !== "session" || state.sessionConfigState.baseAvailable; + var saveBusy = state.configMode === "session" ? state.busy.sessionSave : state.busy.configSave; + var saveAction = state.configMode === "session" ? "save-session" : "save-config"; + var saveText = state.configMode === "session" ? "保存会话配置" : "保存配置"; + if (saveBusy) saveText = "保存中..."; + $("view-config").innerHTML = [ + '
', + '
', + '
', + '', + '', + '
', + '
', + state.configMode === "session" ? '' : "", + '', + '
', + '
', + state.configMode === "session" && selectedMeta ? '' : "", + state.configMode === "session" && state.config ? renderSessionEnableCard() : "", + state.sessionConfigState.message ? '
' + escapeHtml(state.sessionConfigState.message) + '
' : "", + state.saveFeedback ? '' : "", + objectKeys(schema).length ? '
' + fields.join("") + '
' : '
暂无配置 Schema
', + '', + '
' + ].join(""); } - function renderFrame(url) { - var wrap = byId("frame-wrap"); - if (!wrap) return; - wrap.innerHTML = ""; - var frame = document.createElement("iframe"); - frame.title = "主动消息 Web 管理端"; - frame.src = url; - wrap.appendChild(frame); + function renderSessionEnableCard() { + var enabled = Boolean(state.config && state.config.enable); + var label = detectSessionType(state.selectedSession) === "group" ? "群聊会话启用状态" : "私聊会话启用状态"; + return [ + '
', + '
', label, '
', + '
这是当前会话的独立开关。关闭后,该会话会暂停主动消息,但不影响同类型全局配置与其他会话。
', + '
', enabled ? "已启用" : "已暂停", '', + '
', + '
' + ].join(""); } - function render(payload) { - payload = payload || {}; - var status = payload.status || {}; - var webAdmin = payload.web_admin || {}; - var jobs = Array.isArray(payload.jobs) ? payload.jobs : []; - var sessions = Array.isArray(payload.sessions) ? payload.sessions : []; - var url = deriveReachableUrl(webAdmin); - state.webUrl = url; + function setFeedback(type, message) { + state.saveFeedbackType = type || ""; + state.saveFeedback = message || ""; + } - text("page-subtitle", "已连接 Pages,复用现有 Web 管理端"); - text("running-value", status.running ? "运行中" : "未运行"); - text("version-value", "版本 " + (status.version || "--")); - text("jobs-value", jobs.length || status.jobs_count || 0); - text("sessions-value", "会话 " + (sessions.length || status.sessions_count || 0)); - text("web-value", webAdmin.running ? "已启动" : (webAdmin.enabled ? "待启动" : "已禁用")); - text("web-hint", url); + function switchConfigMode(mode) { + if (mode !== "global" && mode !== "session") return; + state.configMode = mode; + state.expandedKeys = []; + setFeedback("", ""); + if (mode === "session" && !state.selectedSession && state.sessions.length) { + state.selectedSession = state.sessions[0].session || state.sessions[0]; + } + loadConfig(); + } - var openButton = byId("open-button"); - if (openButton) openButton.href = url; - renderFrame(url); + function toggleConfigGroup(path) { + var index = state.expandedKeys.indexOf(path); + if (index >= 0) { + state.expandedKeys.splice(index, 1); + } else { + state.expandedKeys.push(path); + } + render(); } - function renderFallback(message) { - text("page-subtitle", message || "未连接 Pages bridge,使用默认 Web 管理端地址"); - text("running-value", "未知"); - text("version-value", "版本 --"); - text("jobs-value", "--"); - text("sessions-value", "会话 --"); - text("web-value", "默认入口"); - text("web-hint", DEFAULT_URL); - var openButton = byId("open-button"); - if (openButton) openButton.href = DEFAULT_URL; - renderFrame(DEFAULT_URL); + function toggleAllConfigGroups() { + if (state.expandedKeys.length) { + state.expandedKeys = []; + } else { + state.expandedKeys = getAllExpandablePathsFromEntries(getConfigEntries(), ""); + } + render(); + } + + function generateDefaults(entries) { + var result = {}; + for (var i = 0; i < entries.length; i += 1) { + var key = entries[i][0]; + var schema = entries[i][1] || {}; + if (schema.type === "object" && schema.items) { + var childEntries = []; + var childKeys = objectKeys(schema.items); + for (var j = 0; j < childKeys.length; j += 1) childEntries.push([childKeys[j], schema.items[childKeys[j]]]); + result[key] = generateDefaults(childEntries); + } else { + result[key] = schemaDefault(schema); + } + } + return result; + } + + function resetConfigDefaults() { + if (!window.confirm("确定要恢复默认值吗?\n\n这会覆盖当前编辑区内容,需要点击保存后才会写入。")) return; + state.config = generateDefaults(getConfigEntries()); + if (state.configMode === "session" && state.config.enable === undefined) { + state.config.enable = true; + } + setFeedback("warn", "已恢复为默认值,点击保存后生效。"); + render(); + } + + function updateConfigFromControl(node, shouldRender, suppressRender) { + if (!state.config || typeof state.config !== "object") state.config = {}; + var path = node.getAttribute("data-config-path"); + var type = node.getAttribute("data-config-type") || "string"; + var value = node.value; + if (type === "bool") { + value = Boolean(node.checked); + shouldRender = true; + } else if (type === "list") { + value = String(value || "").split(/\r?\n/); + } else if (type === "int" || type === "integer") { + if (value === "" || value === "-") return; + value = parseInt(value, 10); + if (isNaN(value)) value = 0; + } else if (type === "number" || type === "float" || type === "double") { + if (value === "" || value === "-") return; + value = parseFloat(value); + if (isNaN(value)) value = 0; + } + setByPath(state.config, path, value); + setFeedback("", ""); + if (node.type === "range") { + var pair = document.querySelector('input[type="number"][data-config-path="' + path.replace(/"/g, '\\"') + '"]'); + if (pair) pair.value = node.value; + } + if (!suppressRender && (shouldRender || type === "select")) render(); + } + + function syncConfigFromVisibleControls() { + if (!state.config || typeof state.config !== "object") state.config = {}; + var root = $("view-config") || document; + var nodes = root.querySelectorAll("[data-config-path]"); + for (var i = 0; i < nodes.length; i += 1) { + updateConfigFromControl(nodes[i], false, true); + } + } + + function cleanConfig(obj) { + if (Array.isArray(obj)) { + var list = []; + for (var i = 0; i < obj.length; i += 1) { + var item = typeof obj[i] === "string" ? obj[i].trim() : obj[i]; + if (item !== "") list.push(item); + } + return list; + } + if (obj && typeof obj === "object") { + var next = {}; + var keys = objectKeys(obj); + for (var k = 0; k < keys.length; k += 1) next[keys[k]] = cleanConfig(obj[keys[k]]); + return next; + } + return obj; + } + + function openDirectory(path) { + apiPost(route("open-directory"), { path: path }).then(function (res) { + window.alert(res.message || "已请求打开目录"); + }).catch(function (err) { + window.alert(err.message || "打开目录失败"); + }); } function loadDashboard() { - if (!state.bridge || typeof state.bridge.apiGet !== "function") { - renderFallback("AstrBot Pages bridge 未注入,使用默认 Web 管理端地址"); - return Promise.resolve(); - } - return Promise.resolve(state.bridge.apiGet("dashboard")) - .then(function (payload) { - state.payload = normalizePayload(payload); - render(state.payload); - }) - .catch(function (error) { - renderFallback(error && error.message ? error.message : "读取 Pages 状态失败"); + if (!state.bridgeReady) { + setError("AstrBot Pages bridge 未注入,当前只能显示静态页面。"); + return; + } + apiGet(route("dashboard")).then(function (data) { + applyDashboardPayload(data); + setError(""); + render(); + }).catch(function () { + Promise.all([apiGet(route("status")), apiGet(route("jobs")), apiGet(route("session-config/sessions"))]).then(function (parts) { + state.status = parts[0] || {}; + state.jobs = asArray(parts[1].jobs || parts[1]); + state.sessions = asArray(parts[2].sessions || parts[2]); + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载状态失败"); }); + }); } - function init() { - var openButton = byId("open-button"); - if (openButton) openButton.href = DEFAULT_URL; - var refreshButton = byId("refresh-button"); - if (refreshButton) refreshButton.addEventListener("click", loadDashboard); + function applyDashboardPayload(data) { + data = data || {}; + state.status = data.status || state.status || {}; + state.jobs = asArray(data.jobs); + state.sessions = asArray(data.sessions); + state.notificationsMeta = data.notifications_meta || state.notificationsMeta || {}; + state.lastRealtimeAt = Date.now(); + } + + function loadRealtimeSnapshot() { + if (!state.bridgeReady || !state.bridge) return; + if (document.visibilityState === "hidden") return; + if (state.busy.configSave || state.busy.sessionSave) return; + if (state.view === "config" || state.view === "docs") return; + apiGet(route("dashboard")).then(function (data) { + applyDashboardPayload(data); + if (state.view === "status" || state.view === "tasks") render(); + }).catch(function () { + Promise.all([apiGet(route("status")), apiGet(route("jobs")), apiGet(route("session-config/sessions"))]).then(function (parts) { + state.status = parts[0] || state.status || {}; + state.jobs = asArray(parts[1].jobs || parts[1]); + state.sessions = asArray(parts[2].sessions || parts[2]); + state.lastRealtimeAt = Date.now(); + if (state.view === "status" || state.view === "tasks") render(); + }).catch(function () {}); + }); + } + + function initRealtimeSync() { + if (state.realtimeTimer) return; + state.realtimeTimer = setInterval(loadRealtimeSnapshot, 1000); + } + + function loadJobs() { + apiGet(route("jobs")).then(function (data) { + state.jobs = asArray(data.jobs || data); + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载任务失败"); + }); + } + + function triggerJob(id) { + if (!id) return; + apiPost(route("jobs/" + encodeURIComponent(id) + "/trigger"), {}).then(loadJobs).catch(function (err) { setError(err.message); }); + } + + function rescheduleJob(id) { + if (!id) return; + apiPost(route("jobs/" + encodeURIComponent(id) + "/reschedule"), {}).then(loadJobs).catch(function (err) { setError(err.message); }); + } + function cancelJob(id) { + if (!id) return; + apiPost(route("jobs-cancel/" + encodeURIComponent(id)), {}).then(loadJobs).catch(function (err) { setError(err.message); }); + } + + function loadNotifications() { + apiGet(route("notifications")).then(function (data) { + state.notifications = asArray(data.items || data.notifications || data); + state.notificationsMeta = data.meta || state.notificationsMeta || {}; + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载通知失败"); + }); + } + + function readNotification(id) { + apiPost(route("notifications/read"), { id: id }).then(loadNotifications).catch(function (err) { setError(err.message); }); + } + + function readAllNotifications() { + apiPost(route("notifications/read-all"), {}).then(loadNotifications).catch(function (err) { setError(err.message); }); + } + + function refreshNotifications() { + apiPost(route("notifications/refresh"), {}).then(loadNotifications).catch(function (err) { setError(err.message); }); + } + + function loadMarkdownFiles() { + apiGet(route("markdown-files")).then(function (data) { + state.markdownFiles = asArray(data.items || data.files || data); + if (!state.selectedMarkdownPath && state.markdownFiles.length) { + state.selectedMarkdownPath = state.markdownFiles[0].path; + loadMarkdownDocument(state.selectedMarkdownPath); + } + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载文档目录失败"); + }); + } + + function loadMarkdownDocument(path) { + if (!path) { + state.markdownDocument = null; + render(); + return; + } + apiGet(route("markdown-files/" + encodeURIComponent(path))).then(function (data) { + state.markdownDocument = data || {}; + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载文档失败"); + }); + } + + function loadConfig() { + Promise.all([ + apiGet(route("get_config")), + apiGet(route("config-schema")), + apiGet(route("session-config/sessions")) + ]).then(function (parts) { + state.configSchema = parts[1] && (parts[1].schema || parts[1]) || {}; + state.sessions = asArray(parts[2].sessions || parts[2]); + if (state.configMode === "session") { + if (!state.selectedSession && state.sessions.length) { + state.selectedSession = state.sessions[0].session || state.sessions[0]; + } + if (state.selectedSession) { + loadSessionDetail(state.selectedSession); + return; + } + state.config = null; + state.sessionConfigState = { baseAvailable: false, message: "请先选择一个会话。" }; + } else { + state.config = parts[0] || {}; + state.sessionConfigState = { baseAvailable: true, message: "" }; + } + if (!state.expandedKeys.length) { + state.expandedKeys = getAllExpandablePathsFromEntries(getConfigEntries(), ""); + } + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载配置失败"); + }); + } + + function saveConfig() { + try { + syncConfigFromVisibleControls(); + var cleaned = cleanConfig(state.config || {}); + state.config = cleaned; + var payload = { + friend_settings: cleaned.friend_settings, + group_settings: cleaned.group_settings, + web_admin: cleaned.web_admin, + notification_settings: cleaned.notification_settings + }; + setBusy("configSave", true); + directBridgePost("save_config", payload).then(function (data) { + if (!data || data.received !== true) { + throw new Error("保存请求没有命中新后端 save_config 接口,请重载插件后再试"); + } + return apiGet(route("get_config")).then(function (serverConfig) { + var mismatched = []; + var keys = ["friend_settings", "group_settings", "web_admin", "notification_settings"]; + for (var i = 0; i < keys.length; i += 1) { + if (!sameConfigValue(serverConfig && serverConfig[keys[i]], payload[keys[i]])) { + mismatched.push(keys[i]); + } + } + if (mismatched.length) { + throw new Error("保存请求已到后端,但配置回读不一致: " + mismatched.join(", ")); + } + state.config = serverConfig || cleaned; + setFeedback("success", "全局配置已保存,后端回读校验通过。"); + setError(""); + render(); + }); + }).catch(function (err) { + setFeedback("error", err.message || "配置保存失败"); + setError(err.message); + }).finally(function () { + setBusy("configSave", false); + }); + } catch (e) { + setError("JSON 格式错误: " + e.message); + } + } + + function loadSessionDetail(session) { + if (!session) { + state.sessionDetail = null; + render(); + return; + } + apiGet(route("session-config/" + encodeURIComponent(session))).then(function (data) { + state.sessionDetail = data || {}; + state.config = state.sessionDetail.effective || state.sessionDetail.override || {}; + state.sessionConfigState = state.sessionDetail.base ? { baseAvailable: true, message: "" } : { + baseAvailable: false, + message: "该会话尚未命中对应类型的全局 session_list,暂时无法保存会话差异配置。请先在对应全局配置中加入该会话。" + }; + if (!state.expandedKeys.length) { + state.expandedKeys = getAllExpandablePathsFromEntries(getConfigEntries(), ""); + } + setError(""); + render(); + }).catch(function (err) { + setError(err.message || "加载会话配置失败"); + }); + } + + function saveSessionConfig() { + if (!state.selectedSession) { + setError("请先选择会话"); + return; + } + try { + syncConfigFromVisibleControls(); + var payload = { mode: "effective", effective: cleanConfig(state.config || {}) }; + setBusy("sessionSave", true); + apiPost(route("session-config-save/" + encodeURIComponent(state.selectedSession)), payload).then(function (data) { + state.sessionDetail = data || {}; + state.sessionDetail.effective = payload.effective; + state.config = payload.effective; + setFeedback("success", "会话差异配置已保存。"); + setError(""); + render(); + }).catch(function (err) { + setFeedback("error", err.message || "会话配置保存失败"); + setError(err.message); + }).finally(function () { + setBusy("sessionSave", false); + }); + } catch (e) { + setError("JSON 格式错误: " + e.message); + } + } + + function resetSessionConfig() { + if (!state.selectedSession) { + setError("请先选择会话"); + return; + } + if (!window.confirm("确定要清空该会话的差异配置吗?\n\n清空后将完全继承全局默认配置。")) return; + apiPost(route("session-config-delete/" + encodeURIComponent(state.selectedSession)), {}).then(function (data) { + state.sessionDetail = data || {}; + state.config = state.sessionDetail.effective || {}; + setFeedback("success", "会话差异配置已清空。"); + loadConfig(); + }).catch(function (err) { setError(err.message); }); + } + + function initClock() { + setInterval(function () { + var node = $("pc-clock"); + if (node) node.textContent = formatDate(new Date()); + if (state.view === "status" && $("view-status")) renderStatus(); + }, 1000); + } + + function init() { + initTheme(); + renderShell(); + hideBoot(); + initClock(); waitBridge(5000).then(function (bridge) { state.bridge = bridge; - if (bridge && typeof bridge.ready === "function") { - Promise.resolve(bridge.ready()).finally(loadDashboard); - } else { - loadDashboard(); + state.bridgeReady = !!bridge; + if (!bridge) { + setError("AstrBot Pages bridge 未注入,当前页面无法读取插件数据。"); + return; } + Promise.resolve(typeof bridge.ready === "function" ? bridge.ready() : null).then(function () { + state.bridgeReady = true; + loadDashboard(); + initRealtimeSync(); + }).catch(function (err) { + state.bridgeReady = false; + setError(err.message || "AstrBot Pages bridge 初始化失败"); + }); }); } From d9f91043d4c57316dec38eaeb9dc4c92ad91af7f Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 21:25:53 +0800 Subject: [PATCH 3/8] chore: document pages bridge save paths --- core/task_scheduler.py | 2 ++ core/web_admin_server.py | 6 ++++++ pages/proactive-chat/page-app.js | 35 +++++++++++++++++++++++++------- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/core/task_scheduler.py b/core/task_scheduler.py index ee90643..389623e 100644 --- a/core/task_scheduler.py +++ b/core/task_scheduler.py @@ -134,6 +134,7 @@ def _is_persisted_task_still_valid( def _get_max_unanswered_count( self, session_config: dict | None, default: int = 3 ) -> int: + """Return the configured unanswered limit, treating non-positive values as disabled.""" if not isinstance(session_config, dict): return default schedule_conf = session_config.get("schedule_settings", {}) @@ -150,6 +151,7 @@ def _is_unanswered_limit_reached( session_config: dict | None, unanswered_count: int | None = None, ) -> bool: + """Shared guard used by runtime scheduling and Pages task snapshots.""" max_unanswered = self._get_max_unanswered_count(session_config) if max_unanswered <= 0: return False diff --git a/core/web_admin_server.py b/core/web_admin_server.py index 2e80bad..249dd68 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -248,6 +248,7 @@ def _register_astrbot_page_route( return False async def _read_astrbot_page_json(self) -> dict[str, Any]: + """Read JSON sent through AstrBot Pages' Quart-based plugin API bridge.""" if quart_request is None: return {} @@ -264,6 +265,7 @@ async def _read_astrbot_page_json(self) -> dict[str, Any]: return self._unwrap_page_payload(payload) def _unwrap_page_payload(self, payload: Any) -> dict[str, Any]: + """Unwrap bridge envelopes such as {"body": {...}} without changing normal JSON.""" if isinstance(payload, str): try: payload = json.loads(payload) @@ -1007,6 +1009,7 @@ def _set_config_section(self, key: str, value: Any) -> None: self.config[key] = value def _save_plugin_config(self) -> tuple[bool, str | None]: + """Persist config through AstrBot's native object and notify newer contexts.""" try: # AstrBot 配置对象通常提供 save_config 方法,这里做鸭子类型兼容。 native = self._native_config or ( @@ -1064,6 +1067,7 @@ def _build_config_payload(self) -> dict[str, Any]: async def _apply_config_payload(self, payload: dict[str, Any]) -> dict[str, Any]: payload = self._unwrap_page_payload(payload) + # Pages 只能写入 WebUI 暴露的四个顶层配置段,避免误把运行时状态写进主配置。 allowed_keys = { "friend_settings", "group_settings", @@ -1153,6 +1157,7 @@ async def _apply_session_config_payload( mode = payload.get("mode", "effective") if mode == "override": + # override 模式直接保存差异片段,适合高级用户或后续批量编辑入口。 override = payload.get("override", {}) if not isinstance(override, dict): return {"ok": False, "error": "override 必须是对象"} @@ -1160,6 +1165,7 @@ async def _apply_session_config_payload( normalized, override ) else: + # effective 模式让前端提交完整会话配置,后端负责计算与全局配置的最小差异。 effective = payload.get("effective", {}) if not isinstance(effective, dict): return {"ok": False, "error": "effective 必须是对象"} diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js index acad01d..b094aa6 100644 --- a/pages/proactive-chat/page-app.js +++ b/pages/proactive-chat/page-app.js @@ -249,6 +249,7 @@ if (!state.bridge || typeof state.bridge.apiPost !== "function") { return Promise.reject(new Error("AstrBot Pages bridge 未注入,请从 AstrBot WebUI 的插件页面重新打开。")); } + // 保存配置走裸 endpoint,用 received 标记确认命中了本插件的新 Pages 后端。 return Promise.resolve(state.bridge.apiPost(endpoint, body || {})).then(normalizePayload); } @@ -1151,6 +1152,19 @@ return obj; } + function stripSessionRuntimeKeys(config) { + var cleaned = cleanConfig(config || {}); + if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) return {}; + // 会话 effective 配置会带运行时元信息,保存和回读校验都只比较真实配置字段。 + var copy = {}; + var keys = objectKeys(cleaned); + for (var i = 0; i < keys.length; i += 1) { + if (keys[i].charAt(0) === "_") continue; + copy[keys[i]] = cleaned[keys[i]]; + } + return copy; + } + function openDirectory(path) { apiPost(route("open-directory"), { path: path }).then(function (res) { window.alert(res.message || "已请求打开目录"); @@ -1335,6 +1349,7 @@ notification_settings: cleaned.notification_settings }; setBusy("configSave", true); + // 保存后立即回读,避免只更新前端状态而后端实际没有持久化。 directBridgePost("save_config", payload).then(function (data) { if (!data || data.received !== true) { throw new Error("保存请求没有命中新后端 save_config 接口,请重载插件后再试"); @@ -1396,15 +1411,21 @@ } try { syncConfigFromVisibleControls(); - var payload = { mode: "effective", effective: cleanConfig(state.config || {}) }; + var effective = stripSessionRuntimeKeys(state.config || {}); + var payload = { mode: "effective", effective: effective }; setBusy("sessionSave", true); apiPost(route("session-config-save/" + encodeURIComponent(state.selectedSession)), payload).then(function (data) { - state.sessionDetail = data || {}; - state.sessionDetail.effective = payload.effective; - state.config = payload.effective; - setFeedback("success", "会话差异配置已保存。"); - setError(""); - render(); + return apiGet(route("session-config/" + encodeURIComponent(state.selectedSession))).then(function (serverDetail) { + var serverEffective = stripSessionRuntimeKeys(serverDetail && serverDetail.effective); + if (!sameConfigValue(serverEffective, payload.effective)) { + throw new Error("会话配置保存请求已到后端,但配置回读不一致"); + } + state.sessionDetail = serverDetail || data || {}; + state.config = state.sessionDetail.effective || payload.effective; + setFeedback("success", "会话差异配置已保存,后端回读校验通过。"); + setError(""); + render(); + }); }).catch(function (err) { setFeedback("error", err.message || "会话配置保存失败"); setError(err.message); From 5babdf8357a01496545f1ecf18b496fa195b1ab2 Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 21:38:07 +0800 Subject: [PATCH 4/8] chore: remove pages directory shortcuts --- core/web_admin_server.py | 5 ----- pages/proactive-chat/page-app.js | 13 ------------- 2 files changed, 18 deletions(-) diff --git a/core/web_admin_server.py b/core/web_admin_server.py index 249dd68..4a4fec9 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -199,7 +199,6 @@ def register_astrbot_page_api(self) -> None: ("notifications/refresh", self._page_refresh_notifications, ["POST"]), ("markdown-files", self._page_list_markdown_files, ["GET"]), ("markdown-files/", self._page_get_markdown_file, ["GET"]), - ("open-directory", self._page_open_directory, ["POST"]), ) registered_count = 0 @@ -385,10 +384,6 @@ async def _page_list_markdown_files(self): async def _page_get_markdown_file(self, file_path: str): return await self._build_markdown_file_payload(file_path) - async def _page_open_directory(self): - payload = await self._read_astrbot_page_json() - return await self._open_directory_payload(payload) - def _setup_app(self) -> None: _patch_starlette_router_startup_kwargs() # 创建 FastAPI 应用,版本号用于控制台元信息展示。 diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js index b094aa6..189dcd3 100644 --- a/pages/proactive-chat/page-app.js +++ b/pages/proactive-chat/page-app.js @@ -297,10 +297,6 @@ '
主动消息
Admin Console
', '', '', - '
', - '', - '', - '
', '', '
@DBJD-CR
', '
🔧 (主动消息) ... 点个 Star 吧~ ⭐
', @@ -362,7 +358,6 @@ function handleAction(action, node) { if (action === "refresh") loadCurrentView(); if (action === "theme") toggleTheme(); - if (action === "open-dir") openDirectory(node.getAttribute("data-path")); if (action === "trigger-job") triggerJob(node.getAttribute("data-id")); if (action === "reschedule-job") rescheduleJob(node.getAttribute("data-id")); if (action === "cancel-job") cancelJob(node.getAttribute("data-id")); @@ -1165,14 +1160,6 @@ return copy; } - function openDirectory(path) { - apiPost(route("open-directory"), { path: path }).then(function (res) { - window.alert(res.message || "已请求打开目录"); - }).catch(function (err) { - window.alert(err.message || "打开目录失败"); - }); - } - function loadDashboard() { if (!state.bridgeReady) { setError("AstrBot Pages bridge 未注入,当前只能显示静态页面。"); From 57c880cb0a94b3a352c553a9d150608db084b5f7 Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 21:45:18 +0800 Subject: [PATCH 5/8] chore: remove pages notifications and docs --- core/web_admin_server.py | 31 ----- pages/proactive-chat/page-app.css | 88 +------------- pages/proactive-chat/page-app.js | 195 +----------------------------- 3 files changed, 4 insertions(+), 310 deletions(-) diff --git a/core/web_admin_server.py b/core/web_admin_server.py index 4a4fec9..dc2ff72 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -189,16 +189,6 @@ def register_astrbot_page_api(self) -> None: ("jobs//reschedule", self._page_reschedule_job, ["POST"]), ("jobs//trigger", self._page_trigger_job, ["POST"]), ("jobs-cancel/", self._page_cancel_job, ["POST"]), - ("notifications", self._page_get_notifications, ["GET"]), - ("notifications/read", self._page_mark_notification_read, ["POST"]), - ( - "notifications/read-all", - self._page_mark_all_notifications_read, - ["POST"], - ), - ("notifications/refresh", self._page_refresh_notifications, ["POST"]), - ("markdown-files", self._page_list_markdown_files, ["GET"]), - ("markdown-files/", self._page_get_markdown_file, ["GET"]), ) registered_count = 0 @@ -365,25 +355,6 @@ async def _page_trigger_job(self, umo: str): async def _page_cancel_job(self, umo: str): return await self._cancel_job_payload(umo) - async def _page_get_notifications(self): - return await self._build_notification_payload() - - async def _page_mark_notification_read(self): - payload = await self._read_astrbot_page_json() - return await self._mark_notification_read_payload(payload) - - async def _page_mark_all_notifications_read(self): - return await self._mark_all_notifications_read_payload() - - async def _page_refresh_notifications(self): - return await self._refresh_notifications_payload() - - async def _page_list_markdown_files(self): - return {"items": self._list_markdown_documents()} - - async def _page_get_markdown_file(self, file_path: str): - return await self._build_markdown_file_payload(file_path) - def _setup_app(self) -> None: _patch_starlette_router_startup_kwargs() # 创建 FastAPI 应用,版本号用于控制台元信息展示。 @@ -1742,13 +1713,11 @@ def _build_web_admin_url(self) -> str | None: async def build_astrbot_page_payload(self) -> dict[str, Any]: """构造 AstrBot 插件卡片 Page 的轻量运行快照。""" - notifications = await self._build_notification_payload() return { "ok": True, "status": self._build_status_payload(), "jobs": self._collect_jobs(), "sessions": self._list_known_session_summaries(), - "notifications_meta": notifications.get("meta", {}), "web_admin": { "available": bool(self._web_admin_available and self.app), "enabled": bool(self.config.get("web_admin", {}).get("enabled", False)), diff --git a/pages/proactive-chat/page-app.css b/pages/proactive-chat/page-app.css index aa1e91c..ebe991c 100644 --- a/pages/proactive-chat/page-app.css +++ b/pages/proactive-chat/page-app.css @@ -735,91 +735,6 @@ body.dark-theme .pc-config-group { color: #0F7A43; } -.pc-doc-layout { - display: grid; - grid-template-columns: 280px minmax(0, 1fr); - gap: 16px; -} - -.pc-doc-body { - max-height: calc(100vh - 190px); - overflow: auto; - line-height: 1.75; -} - -.pc-doc-body h1, -.pc-doc-body h2, -.pc-doc-body h3 { - margin: 1em 0 .5em; -} - -.pc-doc-body p, -.pc-doc-body ul, -.pc-doc-body ol, -.pc-doc-body pre { - margin: 0 0 1em; -} - -.pc-doc-body pre { - overflow: auto; - padding: 12px; - border-radius: 12px; - background: rgba(103, 80, 164, 0.1); -} - -.pc-doc-body code { - padding: 2px 5px; - border-radius: 6px; - background: rgba(103, 80, 164, 0.12); - font-size: .92em; -} - -.pc-doc-body pre code { - display: block; - padding: 0; - background: transparent; - white-space: pre; -} - -.pc-doc-body blockquote { - margin: 0 0 1em; - padding: 10px 14px; - border-left: 4px solid var(--md-sys-color-primary); - border-radius: 8px; - background: rgba(103, 80, 164, 0.08); -} - -.pc-doc-body a { - color: var(--md-sys-color-primary); - font-weight: 700; -} - -.pc-table-wrap { - overflow: auto; - margin: 0 0 1em; - border: 1px solid var(--glass-border); - border-radius: 12px; -} - -.pc-doc-body table { - width: 100%; - border-collapse: collapse; - min-width: 520px; -} - -.pc-doc-body th, -.pc-doc-body td { - padding: 10px 12px; - border-bottom: 1px solid var(--glass-border); - text-align: left; - vertical-align: top; -} - -.pc-doc-body th { - background: rgba(103, 80, 164, 0.1); - font-weight: 850; -} - .pc-footer-note { margin-top: 12px; color: var(--md-sys-color-on-surface-variant); @@ -848,8 +763,7 @@ body.dark-theme .pc-config-group { } .pc-grid.metrics, - .pc-grid.two, - .pc-doc-layout { + .pc-grid.two { grid-template-columns: 1fr; } diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js index 189dcd3..dda9fbf 100644 --- a/pages/proactive-chat/page-app.js +++ b/pages/proactive-chat/page-app.js @@ -10,8 +10,6 @@ status: {}, jobs: [], sessions: [], - notifications: [], - notificationsMeta: {}, config: null, configSchema: null, configMode: "global", @@ -21,9 +19,6 @@ sessionConfigState: { baseAvailable: true, message: "" }, selectedSession: "", sessionDetail: null, - markdownFiles: [], - selectedMarkdownPath: "", - markdownDocument: null, theme: safeStorageGet("theme") || "light", busy: {}, realtimeTimer: null, @@ -33,8 +28,6 @@ var viewMeta = { status: { label: "运行状态", icon: "📊", subtitle: "服务状态、调度概览与会话计时器" }, tasks: { label: "任务管理", icon: "📋", subtitle: "查看、立即触发、重新调度或取消会话任务" }, - notifications: { label: "通知中心", icon: "🔔", subtitle: "同步插件通知、标记已读与快速刷新" }, - docs: { label: "文档浏览", icon: "📚", subtitle: "浏览 README、CHANGELOG 与 docs 目录文档" }, config: { label: "配置管理", icon: "⚙️", subtitle: "编辑全局配置与会话差异配置" } }; @@ -296,7 +289,7 @@ '
', '
主动消息
Admin Console
', '', - '', + '', '
', '
@DBJD-CR
', '
🔧 (主动消息) ... 点个 Star 吧~ ⭐
', @@ -308,8 +301,6 @@ '
', '
', '
', - '
', - '
', '
', '', '', @@ -340,10 +331,6 @@ state.expandedKeys = []; loadSessionDetail(state.selectedSession); } - if (event.target && event.target.id === "doc-select") { - state.selectedMarkdownPath = event.target.value; - loadMarkdownDocument(state.selectedMarkdownPath); - } if (event.target && event.target.getAttribute("data-config-path")) { updateConfigFromControl(event.target, true); } @@ -361,9 +348,6 @@ if (action === "trigger-job") triggerJob(node.getAttribute("data-id")); if (action === "reschedule-job") rescheduleJob(node.getAttribute("data-id")); if (action === "cancel-job") cancelJob(node.getAttribute("data-id")); - if (action === "read-notification") readNotification(node.getAttribute("data-id")); - if (action === "read-all-notifications") readAllNotifications(); - if (action === "refresh-notifications") refreshNotifications(); if (action === "save-config") saveConfig(); if (action === "load-config") loadConfig(); if (action === "save-session") saveSessionConfig(); @@ -385,8 +369,6 @@ function loadCurrentView() { if (state.view === "status") loadDashboard(); if (state.view === "tasks") loadJobs(); - if (state.view === "notifications") loadNotifications(); - if (state.view === "docs") loadMarkdownFiles(); if (state.view === "config") loadConfig(); } @@ -417,7 +399,7 @@ initTheme(); renderHeader(); renderError(); - var keys = ["status", "tasks", "notifications", "docs", "config"]; + var keys = ["status", "tasks", "config"]; for (var i = 0; i < keys.length; i += 1) { var section = $("view-" + keys[i]); if (section) section.classList.toggle("is-active", state.view === keys[i]); @@ -428,8 +410,6 @@ } renderStatus(); renderTasks(); - renderNotifications(); - renderDocs(); renderConfig(); } @@ -680,122 +660,6 @@ $("view-tasks").innerHTML = html.join(""); } - function renderNotifications() { - var items = state.notifications || []; - var meta = state.notificationsMeta || {}; - var html = ['
通知中心
未读 ', Number(meta.unread_count || 0), ' / 总数 ', Number(meta.total_count || items.length || 0), '
']; - if (!items.length) { - html.push('
暂无通知
'); - $("view-notifications").innerHTML = html.join(""); - return; - } - html.push('
'); - for (var i = 0; i < items.length; i += 1) { - var item = items[i] || {}; - var id = item.id == null ? "" : String(item.id); - html.push('
'); - html.push('
', escapeHtml(item.title || item.type || "通知"), '
'); - html.push('
', escapeHtml(item.level || item.severity || "info"), ' · ', escapeHtml(formatDate(item.created_at || item.timestamp || item.time)), '
'); - html.push('
', escapeHtml(item.content || item.message || item.text || ""), '
'); - if (id) html.push('
'); - html.push('
'); - } - html.push('
'); - $("view-notifications").innerHTML = html.join(""); - } - - function renderDocs() { - var files = state.markdownFiles || []; - var doc = state.markdownDocument || {}; - var options = []; - for (var i = 0; i < files.length; i += 1) { - var file = files[i] || {}; - var selected = file.path === state.selectedMarkdownPath ? " selected" : ""; - options.push(''); - } - $("view-docs").innerHTML = [ - '
', - '
文档目录
选择要浏览的 Markdown 文档
', - '', - '
', - '
', - doc.content ? renderMarkdown(doc.content) : '
请选择或刷新文档
', - '
', - '
' - ].join(""); - } - - function renderMarkdown(markdown) { - var source = String(markdown || "").replace(/\r\n/g, "\n"); - var blocks = source.split(/\n{2,}/); - var out = []; - - function inline(value) { - return escapeHtml(value) - .replace(/`([^`]+)`/g, "$1") - .replace(/\*\*([^*]+)\*\*/g, "$1") - .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '
$1'); - } - - function renderTable(block) { - var rows = block.split("\n"); - if (rows.length < 2 || rows[0].indexOf("|") < 0 || !/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(rows[1])) return ""; - var headers = rows[0].split("|").map(function (cell) { return cell.trim(); }).filter(Boolean); - var body = rows.slice(2); - var html = ['
']; - for (var h = 0; h < headers.length; h += 1) html.push(""); - html.push(""); - for (var r = 0; r < body.length; r += 1) { - if (!body[r].trim()) continue; - var cells = body[r].split("|").map(function (cell) { return cell.trim(); }).filter(function (cell, idx, arr) { - return !(idx === 0 && cell === "") && !(idx === arr.length - 1 && cell === ""); - }); - html.push(""); - for (var c = 0; c < headers.length; c += 1) html.push(""); - html.push(""); - } - html.push("
", inline(headers[h]), "
", inline(cells[c] || ""), "
"); - return html.join(""); - } - - for (var i = 0; i < blocks.length; i += 1) { - var block = blocks[i]; - var table = renderTable(block); - if (table) { - out.push(table); - continue; - } - var fence = block.match(/^```([\w-]*)\n([\s\S]*?)```$/); - if (fence) { - out.push('
' + escapeHtml(fence[2]) + "
"); - continue; - } - if (/^###\s+/.test(block)) { - out.push("

" + inline(block.replace(/^###\s+/, "")) + "

"); - continue; - } - if (/^##\s+/.test(block)) { - out.push("

" + inline(block.replace(/^##\s+/, "")) + "

"); - continue; - } - if (/^#\s+/.test(block)) { - out.push("

" + inline(block.replace(/^#\s+/, "")) + "

"); - continue; - } - if (/^\s*[-*]\s+/m.test(block)) { - var items = block.split("\n").filter(Boolean); - out.push("
    " + items.map(function (line) { return "
  • " + inline(line.replace(/^\s*[-*]\s+/, "")) + "
  • "; }).join("") + "
"); - continue; - } - if (/^\s*>\s+/m.test(block)) { - out.push("
" + inline(block.replace(/^\s*>\s?/gm, "")).replace(/\n/g, "
") + "
"); - continue; - } - out.push("

" + inline(block).replace(/\n/g, "
") + "

"); - } - return out.join(""); - } - function detectSessionType(sessionId) { var raw = String(sessionId || ""); if (raw.indexOf(":GroupMessage:") >= 0 || raw.indexOf(":GuildMessage:") >= 0) return "group"; @@ -1187,7 +1051,6 @@ state.status = data.status || state.status || {}; state.jobs = asArray(data.jobs); state.sessions = asArray(data.sessions); - state.notificationsMeta = data.notifications_meta || state.notificationsMeta || {}; state.lastRealtimeAt = Date.now(); } @@ -1195,7 +1058,7 @@ if (!state.bridgeReady || !state.bridge) return; if (document.visibilityState === "hidden") return; if (state.busy.configSave || state.busy.sessionSave) return; - if (state.view === "config" || state.view === "docs") return; + if (state.view === "config") return; apiGet(route("dashboard")).then(function (data) { applyDashboardPayload(data); if (state.view === "status" || state.view === "tasks") render(); @@ -1240,58 +1103,6 @@ apiPost(route("jobs-cancel/" + encodeURIComponent(id)), {}).then(loadJobs).catch(function (err) { setError(err.message); }); } - function loadNotifications() { - apiGet(route("notifications")).then(function (data) { - state.notifications = asArray(data.items || data.notifications || data); - state.notificationsMeta = data.meta || state.notificationsMeta || {}; - setError(""); - render(); - }).catch(function (err) { - setError(err.message || "加载通知失败"); - }); - } - - function readNotification(id) { - apiPost(route("notifications/read"), { id: id }).then(loadNotifications).catch(function (err) { setError(err.message); }); - } - - function readAllNotifications() { - apiPost(route("notifications/read-all"), {}).then(loadNotifications).catch(function (err) { setError(err.message); }); - } - - function refreshNotifications() { - apiPost(route("notifications/refresh"), {}).then(loadNotifications).catch(function (err) { setError(err.message); }); - } - - function loadMarkdownFiles() { - apiGet(route("markdown-files")).then(function (data) { - state.markdownFiles = asArray(data.items || data.files || data); - if (!state.selectedMarkdownPath && state.markdownFiles.length) { - state.selectedMarkdownPath = state.markdownFiles[0].path; - loadMarkdownDocument(state.selectedMarkdownPath); - } - setError(""); - render(); - }).catch(function (err) { - setError(err.message || "加载文档目录失败"); - }); - } - - function loadMarkdownDocument(path) { - if (!path) { - state.markdownDocument = null; - render(); - return; - } - apiGet(route("markdown-files/" + encodeURIComponent(path))).then(function (data) { - state.markdownDocument = data || {}; - setError(""); - render(); - }).catch(function (err) { - setError(err.message || "加载文档失败"); - }); - } - function loadConfig() { Promise.all([ apiGet(route("get_config")), From 0544c952047cbd9c1b8433d3199ebc3987b6516b Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 21:58:07 +0800 Subject: [PATCH 6/8] chore: trim pages stylesheet whitespace --- pages/proactive-chat/css/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/proactive-chat/css/style.css b/pages/proactive-chat/css/style.css index 2b6fb64..193bf08 100644 --- a/pages/proactive-chat/css/style.css +++ b/pages/proactive-chat/css/style.css @@ -3042,7 +3042,7 @@ body.dark-theme .connection-chip { gap: 16px; grid-template-columns: repeat(6, 1fr); } - + .span-4 { grid-column: span 3; } .span-8 { grid-column: span 6; } .span-12 { grid-column: span 6; } From e707217ce2be1527ec33666e7d60020311528491 Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Thu, 11 Jun 2026 22:56:18 +0800 Subject: [PATCH 7/8] fix: address pages console review --- core/task_scheduler.py | 15 +++++++++------ core/web_admin_server.py | 16 +++++++++++++--- pages/proactive-chat/page-app.js | 7 ++++++- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/core/task_scheduler.py b/core/task_scheduler.py index 389623e..2ded579 100644 --- a/core/task_scheduler.py +++ b/core/task_scheduler.py @@ -156,12 +156,15 @@ def _is_unanswered_limit_reached( if max_unanswered <= 0: return False if unanswered_count is None: - try: - unanswered_count = int( - self.session_data.get(session_id, {}).get("unanswered_count", 0) - or 0 - ) - except (TypeError, ValueError): + session_info = self.session_data.get(session_id) + if isinstance(session_info, dict): + try: + unanswered_count = int( + session_info.get("unanswered_count", 0) or 0 + ) + except (TypeError, ValueError): + unanswered_count = 0 + else: unanswered_count = 0 return unanswered_count >= max_unanswered diff --git a/core/web_admin_server.py b/core/web_admin_server.py index dc2ff72..7e88198 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -122,6 +122,7 @@ def __init__(self, plugin: Any): self._token_expire_seconds = 60 * 60 * 24 # 简单的内存令牌表:token -> 过期时间戳。 self._tokens: dict[str, float] = {} + self._background_tasks: set[asyncio.Task] = set() # 仅当配置中设置了密码时才开启鉴权。 self._auth_enabled = bool(self.config.get("web_admin", {}).get("password", "")) # 缓存插件版本,避免在高频状态轮询与广播中重复读取文件。 @@ -152,6 +153,14 @@ def _decode_route_umo(self, umo: str) -> str: except Exception: return str(umo or "") + def _track_background_task(self, task: asyncio.Task) -> None: + track_task = getattr(self.plugin, "_track_task", None) + if callable(track_task): + track_task(task) + return + self._background_tasks.add(task) + task.add_done_callback(self._background_tasks.discard) + def register_astrbot_page_api(self) -> None: """向 AstrBot WebUI 插件 Page 暴露完整管理端接口。""" context = getattr(self.plugin, "context", None) @@ -992,7 +1001,7 @@ def _save_plugin_config(self) -> tuple[bool, str | None]: if callable(update_config): updated = update_config(self.config) if inspect.isawaitable(updated): - asyncio.create_task(updated) + self._track_background_task(asyncio.create_task(updated)) return True, None except Exception as e: logger.warning(f"[主动消息] 保存配置失败喵: {e}") @@ -1032,7 +1041,6 @@ def _build_config_payload(self) -> dict[str, Any]: } async def _apply_config_payload(self, payload: dict[str, Any]) -> dict[str, Any]: - payload = self._unwrap_page_payload(payload) # Pages 只能写入 WebUI 暴露的四个顶层配置段,避免误把运行时状态写进主配置。 allowed_keys = { "friend_settings", @@ -1195,7 +1203,9 @@ async def _trigger_job_payload(self, umo: str) -> dict[str, Any]: } self.plugin.manual_trigger_sessions.add(normalized) - asyncio.create_task(self.plugin.check_and_chat(normalized)) + self._track_background_task( + asyncio.create_task(self.plugin.check_and_chat(normalized)) + ) await self._broadcast_update("jobs") return { "ok": True, diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js index dda9fbf..d6307a7 100644 --- a/pages/proactive-chat/page-app.js +++ b/pages/proactive-chat/page-app.js @@ -22,6 +22,7 @@ theme: safeStorageGet("theme") || "light", busy: {}, realtimeTimer: null, + loadingSnapshot: false, lastRealtimeAt: 0 }; @@ -1056,20 +1057,24 @@ function loadRealtimeSnapshot() { if (!state.bridgeReady || !state.bridge) return; + if (state.loadingSnapshot) return; if (document.visibilityState === "hidden") return; if (state.busy.configSave || state.busy.sessionSave) return; if (state.view === "config") return; + state.loadingSnapshot = true; apiGet(route("dashboard")).then(function (data) { applyDashboardPayload(data); if (state.view === "status" || state.view === "tasks") render(); }).catch(function () { - Promise.all([apiGet(route("status")), apiGet(route("jobs")), apiGet(route("session-config/sessions"))]).then(function (parts) { + return Promise.all([apiGet(route("status")), apiGet(route("jobs")), apiGet(route("session-config/sessions"))]).then(function (parts) { state.status = parts[0] || state.status || {}; state.jobs = asArray(parts[1].jobs || parts[1]); state.sessions = asArray(parts[2].sessions || parts[2]); state.lastRealtimeAt = Date.now(); if (state.view === "status" || state.view === "tasks") render(); }).catch(function () {}); + }).finally(function () { + state.loadingSnapshot = false; }); } From f64bd3f3b5f7e362969271eb04fe441397905b42 Mon Sep 17 00:00:00 2001 From: Justice-ocr Date: Fri, 12 Jun 2026 18:33:45 +0800 Subject: [PATCH 8/8] fix: stop scheduling after unanswered limit --- core/chat_flow.py | 70 +++++++++++++++++++++----------- core/task_scheduler.py | 34 ++++++++++++++++ core/web_admin_server.py | 30 +++++++------- pages/proactive-chat/page-app.js | 2 - 4 files changed, 94 insertions(+), 42 deletions(-) diff --git a/core/chat_flow.py b/core/chat_flow.py index c370a8d..fa544b9 100644 --- a/core/chat_flow.py +++ b/core/chat_flow.py @@ -88,6 +88,7 @@ async def _finalize_and_reschedule( ) session_config = None scheduled_job_payload = None + limit_reached_after_send = False async with self.data_lock: # 更新未回复计数器 @@ -106,33 +107,54 @@ async def _finalize_and_reschedule( if not session_config: return - schedule_conf = session_config.get("schedule_settings", {}) - min_interval = int(schedule_conf.get("min_interval_minutes", 30)) * 60 - max_interval = max( - min_interval, - int(schedule_conf.get("max_interval_minutes", 900)) * 60, - ) - # 私聊采用配置区间内随机间隔,减少触发规律性 - random_interval = random.randint(min_interval, max_interval) - scheduled_at = time.time() - next_trigger_time = scheduled_at + random_interval - run_date = datetime.fromtimestamp(next_trigger_time, tz=self.timezone) - - session_payload = self.session_data.setdefault(session_id, {}) - session_payload["next_trigger_time"] = next_trigger_time - session_payload["last_scheduled_at"] = scheduled_at - session_payload["last_schedule_min_interval_seconds"] = min_interval - session_payload["last_schedule_max_interval_seconds"] = max_interval - session_payload["last_schedule_random_interval_seconds"] = ( - random_interval - ) - scheduled_job_payload = { - "run_date": run_date, - "session_config": session_config, - } + if self._is_unanswered_limit_reached( + session_id, session_config, new_unanswered_count + ): + limit_reached_after_send = True + self._clear_session_schedule_state(session_id) + logger.info( + f"[主动消息] {self._get_session_log_str(session_id, session_config)} 的未回复次数已达到上限,停止安排下一次主动消息喵。" + ) + else: + schedule_conf = session_config.get("schedule_settings", {}) + min_interval = ( + int(schedule_conf.get("min_interval_minutes", 30)) * 60 + ) + max_interval = max( + min_interval, + int(schedule_conf.get("max_interval_minutes", 900)) * 60, + ) + # 私聊采用配置区间内随机间隔,减少触发规律性 + random_interval = random.randint(min_interval, max_interval) + scheduled_at = time.time() + next_trigger_time = scheduled_at + random_interval + run_date = datetime.fromtimestamp( + next_trigger_time, tz=self.timezone + ) + + session_payload = self.session_data.setdefault(session_id, {}) + session_payload["next_trigger_time"] = next_trigger_time + session_payload["last_scheduled_at"] = scheduled_at + session_payload["last_schedule_min_interval_seconds"] = ( + min_interval + ) + session_payload["last_schedule_max_interval_seconds"] = ( + max_interval + ) + session_payload["last_schedule_random_interval_seconds"] = ( + random_interval + ) + scheduled_job_payload = { + "run_date": run_date, + "session_config": session_config, + } await self._save_data_internal() + if limit_reached_after_send: + self._purge_related_jobs(session_id) + return + if scheduled_job_payload is not None: self.scheduler.add_job( self.check_and_chat, diff --git a/core/task_scheduler.py b/core/task_scheduler.py index 2ded579..9ab4255 100644 --- a/core/task_scheduler.py +++ b/core/task_scheduler.py @@ -195,6 +195,10 @@ def _clear_session_schedule_state( "last_schedule_min_interval_seconds", "last_schedule_max_interval_seconds", "last_schedule_random_interval_seconds", + "last_schedule_strategy", + "last_schedule_reason", + "last_schedule_rule", + "last_schedule_source", } changed = False @@ -228,6 +232,18 @@ def _purge_related_jobs(self, session_id: str) -> None: except Exception: pass + def _remove_schedule_for_limit_reached( + self, session_id: str, session_config: dict | None = None + ) -> bool: + """达到未回复上限时清理调度器任务和持久化调度字段。""" + normalized_session_id = self._normalize_session_id(session_id) + self._purge_related_jobs(normalized_session_id) + changed = self._clear_session_schedule_state(normalized_session_id) + logger.info( + f"[主动消息] {self._get_session_log_str(normalized_session_id, session_config)} 已达到未回复次数上限,已清理待触发任务并暂停调度喵。" + ) + return changed + def _has_related_persisted_task(self, session_id: str) -> bool: """判断同一目标是否存在仍可恢复的持久化任务(避免重复触发)。""" parsed = self._parse_session_id(session_id) @@ -457,6 +473,14 @@ async def _init_jobs_from_data(self) -> None: ) continue + if self._is_unanswered_limit_reached(session_id, session_config): + if self._clear_session_schedule_state(session_id): + cleaned_runtime_state += 1 + logger.info( + f"[主动消息] {self._get_session_log_str(session_id, session_config)} 的未回复次数已达到上限,跳过恢复定时任务喵。" + ) + continue + try: run_date = datetime.fromtimestamp(next_trigger, tz=self.timezone) existing_job = self.scheduler.get_job(session_id) @@ -536,6 +560,16 @@ async def _schedule_next_chat_and_save( "unanswered_count" ] = 0 + if not reset_counter and self._is_unanswered_limit_reached( + normalized_session_id, session_config + ): + changed = self._remove_schedule_for_limit_reached( + normalized_session_id, session_config + ) + if changed: + await self._save_data_internal() + return + # 计算随机触发时间 min_interval = int(schedule_conf.get("min_interval_minutes", 30)) * 60 max_interval = max( diff --git a/core/web_admin_server.py b/core/web_admin_server.py index 7e88198..c7c072a 100644 --- a/core/web_admin_server.py +++ b/core/web_admin_server.py @@ -889,9 +889,9 @@ async def cancel_job(umo: str): async with self.plugin.data_lock: if normalized in self.plugin.session_data: - # 同步清理持久化数据中的 next_trigger_time,避免界面显示过期信息。 - self.plugin.session_data[normalized].pop("next_trigger_time", None) - await self.plugin._save_data_internal() + # 同步清理持久化调度字段,避免界面显示过期倒计时。 + if self.plugin._clear_session_schedule_state(normalized): + await self.plugin._save_data_internal() if removed: logger.info( @@ -1225,8 +1225,8 @@ async def _cancel_job_payload(self, umo: str) -> dict[str, Any]: async with self.plugin.data_lock: if normalized in self.plugin.session_data: - self.plugin.session_data[normalized].pop("next_trigger_time", None) - await self.plugin._save_data_internal() + if self.plugin._clear_session_schedule_state(normalized): + await self.plugin._save_data_internal() await self._broadcast_update("jobs") return {"ok": True, "session": normalized, "removed": removed} @@ -1678,6 +1678,7 @@ def _build_status_payload(self) -> dict[str, Any]: now = time.time() uptime_sec = max(0, int(now - self.plugin.plugin_start_time)) timer_cards = self._collect_timer_cards(now) + visible_jobs_count = len(self._collect_jobs()) return { "running": True, @@ -1698,9 +1699,7 @@ def _build_status_payload(self) -> dict[str, Any]: "sessions_count": len(self.plugin.session_data), "auto_trigger_timers": len(self.plugin.auto_trigger_timers), "group_timers": len(self.plugin.group_timers), - "jobs_count": len(self.plugin.scheduler.get_jobs()) - if self.plugin.scheduler - else 0, + "jobs_count": visible_jobs_count, # 计时器总数在前端可直接用于角标和标题,无需再做两次求和。 "timer_cards_total": len(timer_cards["auto_trigger_cards"]) + len(timer_cards["group_timer_cards"]), @@ -1749,7 +1748,6 @@ def _collect_jobs(self) -> list[dict[str, Any]]: session_config = self.plugin._get_session_config(session_id) or {} schedule_settings = session_config.get("schedule_settings", {}) context_settings = session_config.get("context_settings", {}) - auto_trigger_settings = session_config.get("auto_trigger_settings", {}) unanswered_count = session_data.get("unanswered_count", 0) if self.plugin._is_unanswered_limit_reached( normalized_session_id, session_config, unanswered_count @@ -1770,9 +1768,8 @@ def _collect_jobs(self) -> list[dict[str, Any]]: "source_mode": context_settings.get( "source_mode", "conversation_history" ), - "max_unanswered_times": schedule_settings.get( - "max_unanswered_times", - auto_trigger_settings.get("max_unanswered_times", 0), + "max_unanswered_times": self.plugin._get_max_unanswered_count( + session_config ), # APScheduler 的 next_run_time 是 datetime,这里统一序列化为 ISO 字符串。 "next_run_time": ( @@ -1824,7 +1821,6 @@ def _collect_jobs(self) -> list[dict[str, Any]]: ) schedule_settings = session_config.get("schedule_settings", {}) context_settings = session_config.get("context_settings", {}) - auto_trigger_settings = session_config.get("auto_trigger_settings", {}) unanswered_count = session_data.get("unanswered_count", 0) if self.plugin._is_unanswered_limit_reached( normalized_session_id, session_config, unanswered_count @@ -1858,9 +1854,8 @@ def _collect_jobs(self) -> list[dict[str, Any]]: "source_mode": context_settings.get( "source_mode", "conversation_history" ), - "max_unanswered_times": schedule_settings.get( - "max_unanswered_times", - auto_trigger_settings.get("max_unanswered_times", 0), + "max_unanswered_times": self.plugin._get_max_unanswered_count( + session_config ), "next_run_time": next_run_time, "unanswered_count": unanswered_count, @@ -1940,6 +1935,9 @@ def _list_known_session_summaries(self) -> list[dict[str, Any]]: "unanswered_count": self.plugin.session_data.get(session, {}).get( "unanswered_count", 0 ), + "max_unanswered_times": self.plugin._get_max_unanswered_count( + effective + ), "manual_trigger_in_progress": session in self.plugin.manual_trigger_sessions, "enabled": bool(effective and effective.get("enable", False)), diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js index d6307a7..fb921c9 100644 --- a/pages/proactive-chat/page-app.js +++ b/pages/proactive-chat/page-app.js @@ -481,7 +481,6 @@ var fallbackSessions = {}; var fallback = []; var jobs = asArray(state.jobs); - var sessions = asArray(state.sessions); function cardSessionId(card) { return String(card && (card.session_id || card.session || card.id) || ""); @@ -544,7 +543,6 @@ } for (var j = 0; j < jobs.length; j += 1) pushFallback(jobs[j], "scheduled_job"); - for (var s = 0; s < sessions.length; s += 1) pushFallback(sessions[s], "session_next_trigger"); fallback.sort(function (left, right) { return Number(left.remaining_seconds || 0) - Number(right.remaining_seconds || 0); });
-
-
- -
-

主动消息管理端

-
正在连接 AstrBot Pages...
-
-
-
-
-
- -
-
正在加载管理端...
-
-
+
+ +
主动消息管理端
+
正在初始化 Pages 控制台...
+
+
+
- + +
+ + diff --git a/pages/proactive-chat/logo.png b/pages/proactive-chat/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ddc92c5398a6a8e20d76ba7ee1fcad130464e1d6 GIT binary patch literal 69222 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

%P*@*+Aa~ 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 X(5F-yM}B~q00000NkvXXu0mjfg6`9M literal 0 HcmV?d00001 diff --git a/pages/proactive-chat/page-app.css b/pages/proactive-chat/page-app.css new file mode 100644 index 0000000..aa1e91c --- /dev/null +++ b/pages/proactive-chat/page-app.css @@ -0,0 +1,900 @@ +.pc-app { + min-height: 100vh; + display: grid; + grid-template-columns: 280px minmax(0, 1fr); + color: var(--md-sys-color-on-surface); +} + +.pc-sidebar { + position: sticky; + top: 0; + height: 100vh; + padding: 22px 18px; + border-right: 1px solid var(--glass-border); + background: linear-gradient(180deg, var(--glass-bg), rgba(255, 255, 255, 0.42)); + backdrop-filter: blur(18px); + overflow-y: auto; +} + +body.dark-theme .pc-sidebar { + background: linear-gradient(180deg, var(--glass-bg), rgba(28, 27, 31, 0.48)); +} + +.pc-brand { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 24px; +} + +.pc-logo { + width: 54px; + height: 54px; + border-radius: 16px; + object-fit: cover; + box-shadow: 0 10px 28px rgba(103, 80, 164, 0.22); +} + +.pc-logo-mark { + width: 54px; + height: 54px; + display: grid; + place-items: center; + flex: 0 0 auto; + border-radius: 16px; + background: + radial-gradient(circle at 30% 24%, rgba(255, 255, 255, 0.94), transparent 28px), + linear-gradient(135deg, var(--md-sys-color-primary), #14b8a6 52%, #f59e0b); + color: #fff; + box-shadow: 0 10px 28px rgba(103, 80, 164, 0.22); +} + +.pc-logo-mark span { + width: 34px; + height: 34px; + display: grid; + place-items: center; + border-radius: 11px; + background: rgba(255, 255, 255, 0.22); + font-size: 22px; + font-weight: 900; +} + +.pc-brand-title { + font-size: 20px; + font-weight: 800; + line-height: 1.1; +} + +.pc-brand-subtitle { + margin-top: 4px; + color: var(--md-sys-color-on-surface-variant); + font-size: 12px; + font-weight: 700; + letter-spacing: .08em; + text-transform: uppercase; +} + +.pc-nav { + display: grid; + gap: 8px; +} + +.pc-nav-button, +.pc-icon-button, +.pc-button { + appearance: none; + border: 0; + font: inherit; + cursor: pointer; +} + +.pc-nav-button { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + min-height: 46px; + padding: 0 14px; + border-radius: 16px; + background: transparent; + color: var(--md-sys-color-on-surface-variant); + font-weight: 700; + text-align: left; +} + +.pc-nav-button:hover, +.pc-nav-button.is-active { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); +} + +.pc-nav-icon { + width: 24px; + text-align: center; + font-size: 18px; +} + +.pc-sidebar-actions { + display: grid; + gap: 10px; + margin: 24px 0; +} + +.pc-github-card { + display: block; + padding: 14px; + border: 1px solid var(--glass-border); + border-radius: 20px; + background: var(--glass-bg); + color: inherit; + text-decoration: none; + box-shadow: var(--glass-shadow); +} + +.pc-github-author { + color: var(--md-sys-color-on-surface-variant); + font-size: 12px; + font-weight: 700; +} + +.pc-github-title { + margin-top: 4px; + font-size: 13px; + font-weight: 800; +} + +.pc-main { + min-width: 0; + display: flex; + flex-direction: column; + height: 100vh; +} + +.pc-topbar { + min-height: var(--top-bar-height); + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 18px 28px; + border-bottom: 1px solid var(--glass-border); + background: rgba(254, 247, 255, 0.72); + backdrop-filter: blur(18px); +} + +body.dark-theme .pc-topbar { + background: rgba(20, 18, 24, 0.72); +} + +.pc-title { + font-size: 26px; + font-weight: 850; +} + +.pc-subtitle { + margin-top: 4px; + color: var(--md-sys-color-on-surface-variant); + font-size: 13px; +} + +.pc-topbar-actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.pc-chip { + display: inline-flex; + align-items: center; + gap: 8px; + min-height: 34px; + padding: 0 12px; + border: 1px solid var(--glass-border); + border-radius: 999px; + background: var(--glass-bg); + color: var(--md-sys-color-on-surface-variant); + font-size: 13px; + font-weight: 700; +} + +.pc-chip.is-ok { + color: #0F7A43; + background: rgba(15, 122, 67, 0.1); +} + +.pc-chip.is-warn { + color: #9A3412; + background: rgba(154, 52, 18, 0.1); +} + +.pc-icon-button { + width: 38px; + height: 38px; + border-radius: 14px; + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); + font-weight: 900; +} + +.pc-content { + flex: 1; + min-height: 0; + overflow: auto; + padding: 24px 28px 34px; +} + +.pc-section { + display: none; +} + +.pc-section.is-active { + display: block; +} + +.pc-grid { + display: grid; + gap: 16px; +} + +.pc-grid.metrics { + grid-template-columns: repeat(4, minmax(160px, 1fr)); + margin-bottom: 18px; +} + +.pc-grid.two { + grid-template-columns: minmax(0, 1fr) minmax(320px, .8fr); +} + +.pc-card { + border: 1px solid var(--glass-border); + border-radius: 20px; + background: var(--glass-bg); + box-shadow: var(--glass-shadow); + padding: 18px; +} + +.pc-card-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 14px; +} + +.pc-card-title { + font-size: 16px; + font-weight: 850; +} + +.pc-card-subtitle { + margin-top: 4px; + color: var(--md-sys-color-on-surface-variant); + font-size: 13px; + line-height: 1.55; +} + +.pc-metric { + min-height: 118px; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.pc-metric-label { + color: var(--md-sys-color-on-surface-variant); + font-size: 13px; + font-weight: 700; +} + +.pc-metric-value { + margin-top: 16px; + font-size: 30px; + font-weight: 900; +} + +.pc-list { + display: grid; + gap: 12px; +} + +.pc-row { + display: grid; + gap: 8px; + padding: 14px; + border: 1px solid var(--glass-border); + border-radius: 16px; + background: rgba(255, 255, 255, 0.42); +} + +body.dark-theme .pc-row { + background: rgba(255, 255, 255, 0.04); +} + +.pc-row-title { + font-weight: 850; + word-break: break-word; +} + +.pc-row-meta { + color: var(--md-sys-color-on-surface-variant); + font-size: 13px; + line-height: 1.6; + word-break: break-word; +} + +.pc-row-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.pc-inline-chip { + display: inline-flex; + align-items: center; + margin-left: 8px; + padding: 2px 8px; + border-radius: 999px; + font-size: 12px; + font-weight: 850; + vertical-align: middle; +} + +.pc-inline-chip.is-ok { + background: rgba(20, 120, 80, 0.14); + color: #147850; +} + +.pc-inline-chip.is-warn { + background: rgba(180, 110, 20, 0.14); + color: #9a5f00; +} + +.pc-timer-list { + display: grid; + gap: 14px; +} + +.pc-timer-card { + display: grid; + gap: 12px; + padding: 16px; + border: 1px solid var(--glass-border); + border-radius: 16px; + background: rgba(255, 255, 255, 0.46); +} + +body.dark-theme .pc-timer-card { + background: rgba(255, 255, 255, 0.05); +} + +.pc-timer-card.is-pending { + border-style: dashed; +} + +.pc-timer-card.is-pending .pc-timer-countdown { + color: var(--md-sys-color-on-surface-variant); +} + +.pc-timer-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.pc-timer-countdown { + font-size: 30px; + font-weight: 900; + color: var(--md-sys-color-primary); +} + +.pc-timer-countdown span { + color: var(--md-sys-color-on-surface-variant); + font-size: 14px; + font-weight: 800; +} + +.pc-progress { + height: 10px; + overflow: hidden; + border-radius: 999px; + background: rgba(103, 80, 164, 0.14); +} + +.pc-progress > div { + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, var(--md-sys-color-primary), #0F7A43); + transition: width .25s ease; +} + +.pc-button { + min-height: 38px; + padding: 0 14px; + border-radius: 14px; + background: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + font-weight: 800; +} + +.pc-button.secondary { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); +} + +.pc-button.ghost { + border: 1px solid var(--glass-border); + background: transparent; + color: var(--md-sys-color-on-surface); +} + +.pc-empty { + padding: 30px 16px; + color: var(--md-sys-color-on-surface-variant); + text-align: center; + border: 1px dashed var(--glass-border); + border-radius: 18px; +} + +.pc-error { + margin-bottom: 16px; + padding: 12px 14px; + border-radius: 14px; + background: rgba(179, 38, 30, 0.1); + color: #B3261E; + font-weight: 700; +} + +.pc-textarea, +.pc-input, +.pc-select { + width: 100%; + border: 1px solid var(--glass-border); + border-radius: 14px; + background: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface); + font: inherit; +} + +.pc-textarea { + min-height: 280px; + padding: 14px; + resize: vertical; + font-family: Consolas, "SFMono-Regular", monospace; + font-size: 13px; + line-height: 1.55; +} + +.pc-input, +.pc-select { + height: 42px; + padding: 0 12px; +} + +.pc-form-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: end; +} + +.pc-config-card { + padding: 0; + overflow: hidden; +} + +.pc-config-toolbar, +.pc-config-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + padding: 16px 18px; + border-bottom: 1px solid var(--glass-border); +} + +.pc-config-footer { + border-top: 1px solid var(--glass-border); + border-bottom: 0; + flex-wrap: wrap; +} + +.pc-config-actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.pc-segmented { + display: inline-flex; + padding: 4px; + border: 1px solid var(--glass-border); + border-radius: 16px; + background: var(--md-sys-color-surface); +} + +.pc-segmented button { + min-height: 34px; + padding: 0 14px; + border: 0; + border-radius: 12px; + background: transparent; + color: var(--md-sys-color-on-surface-variant); + cursor: pointer; + font: inherit; + font-weight: 800; +} + +.pc-segmented button.is-active { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); +} + +.session-picker { + min-width: 360px; +} + +.pc-config-form { + display: grid; + gap: 12px; + padding: 18px; +} + +.pc-config-group { + border: 1px solid var(--glass-border); + border-radius: 16px; + background: rgba(255, 255, 255, 0.34); + overflow: hidden; +} + +body.dark-theme .pc-config-group { + background: rgba(255, 255, 255, 0.04); +} + +.pc-config-group.depth-0 { + border-color: rgba(103, 80, 164, 0.28); +} + +.pc-config-summary { + width: 100%; + min-height: 54px; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 12px; + padding: 12px 14px; + border: 0; + background: rgba(103, 80, 164, 0.07); + color: var(--md-sys-color-on-surface); + cursor: pointer; + font: inherit; + text-align: left; +} + +.pc-config-summary:hover { + background: rgba(103, 80, 164, 0.12); +} + +.pc-config-arrow { + width: 22px; + color: var(--md-sys-color-primary); + font-weight: 900; + text-align: center; +} + +.pc-config-title, +.pc-field-title { + display: block; + font-weight: 850; +} + +.pc-config-hint, +.pc-field-hint { + display: block; + margin-top: 4px; + color: var(--md-sys-color-on-surface-variant); + font-size: 12px; + line-height: 1.55; +} + +.pc-config-count { + padding: 3px 8px; + border: 1px solid var(--glass-border); + border-radius: 999px; + color: var(--md-sys-color-primary); + font-size: 12px; + font-weight: 800; + white-space: nowrap; +} + +.pc-config-children { + display: grid; + gap: 10px; + padding: 12px; +} + +.pc-config-field, +.pc-session-enable { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(220px, 360px); + gap: 16px; + align-items: center; + padding: 14px; + border: 1px solid var(--glass-border); + border-radius: 14px; + background: var(--md-sys-color-surface); +} + +.pc-config-field.depth-0 { + grid-template-columns: minmax(0, 1fr) minmax(260px, 460px); +} + +.pc-session-enable { + margin: 14px 18px 0; + border-color: rgba(103, 80, 164, 0.22); + background: rgba(103, 80, 164, 0.06); +} + +.pc-field-copy { + min-width: 0; +} + +.pc-field-control { + min-width: 0; +} + +.pc-number-field { + display: grid; + grid-template-columns: minmax(120px, 1fr) 110px; + gap: 10px; + align-items: center; +} + +.pc-number-field .pc-input:only-child { + grid-column: 1 / -1; +} + +.pc-range { + width: 100%; + accent-color: var(--md-sys-color-primary); +} + +.pc-textarea.compact { + min-height: 96px; +} + +.pc-textarea.prompt { + min-height: 180px; +} + +.pc-switch { + position: relative; + display: inline-flex; + width: 52px; + height: 30px; + flex-shrink: 0; +} + +.pc-switch input { + position: absolute; + opacity: 0; + pointer-events: none; +} + +.pc-switch span { + position: absolute; + inset: 0; + border-radius: 999px; + background: rgba(120, 113, 108, 0.28); + transition: background .18s ease; +} + +.pc-switch span::after { + content: ""; + position: absolute; + top: 4px; + left: 4px; + width: 22px; + height: 22px; + border-radius: 50%; + background: #fff; + box-shadow: 0 2px 6px rgba(0, 0, 0, .24); + transition: transform .18s ease; +} + +.pc-switch input:checked + span { + background: var(--md-sys-color-primary); +} + +.pc-switch input:checked + span::after { + transform: translateX(22px); +} + +.pc-warning, +.pc-feedback { + margin: 14px 18px 0; + padding: 11px 13px; + border-radius: 14px; + font-size: 13px; + font-weight: 700; +} + +.pc-warning, +.pc-feedback.warn { + background: rgba(154, 52, 18, 0.1); + color: #9A3412; +} + +.pc-feedback.success { + background: rgba(15, 122, 67, 0.1); + color: #0F7A43; +} + +.pc-doc-layout { + display: grid; + grid-template-columns: 280px minmax(0, 1fr); + gap: 16px; +} + +.pc-doc-body { + max-height: calc(100vh - 190px); + overflow: auto; + line-height: 1.75; +} + +.pc-doc-body h1, +.pc-doc-body h2, +.pc-doc-body h3 { + margin: 1em 0 .5em; +} + +.pc-doc-body p, +.pc-doc-body ul, +.pc-doc-body ol, +.pc-doc-body pre { + margin: 0 0 1em; +} + +.pc-doc-body pre { + overflow: auto; + padding: 12px; + border-radius: 12px; + background: rgba(103, 80, 164, 0.1); +} + +.pc-doc-body code { + padding: 2px 5px; + border-radius: 6px; + background: rgba(103, 80, 164, 0.12); + font-size: .92em; +} + +.pc-doc-body pre code { + display: block; + padding: 0; + background: transparent; + white-space: pre; +} + +.pc-doc-body blockquote { + margin: 0 0 1em; + padding: 10px 14px; + border-left: 4px solid var(--md-sys-color-primary); + border-radius: 8px; + background: rgba(103, 80, 164, 0.08); +} + +.pc-doc-body a { + color: var(--md-sys-color-primary); + font-weight: 700; +} + +.pc-table-wrap { + overflow: auto; + margin: 0 0 1em; + border: 1px solid var(--glass-border); + border-radius: 12px; +} + +.pc-doc-body table { + width: 100%; + border-collapse: collapse; + min-width: 520px; +} + +.pc-doc-body th, +.pc-doc-body td { + padding: 10px 12px; + border-bottom: 1px solid var(--glass-border); + text-align: left; + vertical-align: top; +} + +.pc-doc-body th { + background: rgba(103, 80, 164, 0.1); + font-weight: 850; +} + +.pc-footer-note { + margin-top: 12px; + color: var(--md-sys-color-on-surface-variant); + font-size: 12px; +} + +@media (max-width: 980px) { + .pc-app { + grid-template-columns: 1fr; + } + + .pc-sidebar { + position: relative; + height: auto; + border-right: 0; + border-bottom: 1px solid var(--glass-border); + } + + .pc-nav { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .pc-main { + height: auto; + min-height: 100vh; + } + + .pc-grid.metrics, + .pc-grid.two, + .pc-doc-layout { + grid-template-columns: 1fr; + } + + .pc-config-field, + .pc-config-field.depth-0, + .pc-session-enable { + grid-template-columns: 1fr; + } + + .session-picker { + min-width: 0; + width: 100%; + } +} + +@media (max-width: 620px) { + .pc-topbar { + align-items: flex-start; + flex-direction: column; + padding: 16px; + } + + .pc-content { + padding: 16px; + } + + .pc-nav { + grid-template-columns: 1fr; + } + + .pc-config-toolbar { + align-items: stretch; + flex-direction: column; + } + + .pc-config-actions, + .pc-segmented { + width: 100%; + } + + .pc-segmented button { + flex: 1; + } + + .pc-number-field { + grid-template-columns: 1fr; + } +} diff --git a/pages/proactive-chat/page-app.js b/pages/proactive-chat/page-app.js index a77aa01..acad01d 100644 --- a/pages/proactive-chat/page-app.js +++ b/pages/proactive-chat/page-app.js @@ -1,29 +1,211 @@ (function () { "use strict"; - var DEFAULT_URL = "http://127.0.0.1:4100/"; + var PLUGIN_REPO = "https://github.com/DBJD-CR/astrbot_plugin_proactive_chat"; var state = { + view: "status", bridge: null, - payload: null, - webUrl: DEFAULT_URL + bridgeReady: false, + error: "", + status: {}, + jobs: [], + sessions: [], + notifications: [], + notificationsMeta: {}, + config: null, + configSchema: null, + configMode: "global", + expandedKeys: [], + saveFeedback: "", + saveFeedbackType: "", + sessionConfigState: { baseAvailable: true, message: "" }, + selectedSession: "", + sessionDetail: null, + markdownFiles: [], + selectedMarkdownPath: "", + markdownDocument: null, + theme: safeStorageGet("theme") || "light", + busy: {}, + realtimeTimer: null, + lastRealtimeAt: 0 }; - function byId(id) { + var viewMeta = { + status: { label: "运行状态", icon: "📊", subtitle: "服务状态、调度概览与会话计时器" }, + tasks: { label: "任务管理", icon: "📋", subtitle: "查看、立即触发、重新调度或取消会话任务" }, + notifications: { label: "通知中心", icon: "🔔", subtitle: "同步插件通知、标记已读与快速刷新" }, + docs: { label: "文档浏览", icon: "📚", subtitle: "浏览 README、CHANGELOG 与 docs 目录文档" }, + config: { label: "配置管理", icon: "⚙️", subtitle: "编辑全局配置与会话差异配置" } + }; + + function safeStorageGet(key) { + try { + return localStorage.getItem(key); + } catch (e) { + return ""; + } + } + + function safeStorageSet(key, value) { + try { + localStorage.setItem(key, value); + } catch (e) {} + } + + function $(id) { return document.getElementById(id); } - function text(id, value) { - var node = byId(id); - if (node) node.textContent = value == null || value === "" ? "--" : String(value); + function escapeHtml(value) { + return String(value == null ? "" : value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function text(value, fallback) { + if (value === null || value === undefined || value === "") return fallback || "--"; + return String(value); + } + + function asArray(value) { + return Array.isArray(value) ? value : []; + } + + function objectKeys(value) { + return value && typeof value === "object" ? Object.keys(value) : []; + } + + function closest(node, selector) { + while (node && node !== document) { + if (node.matches && node.matches(selector)) return node; + node = node.parentNode; + } + return null; + } + + function parsePath(path) { + return String(path || "").split(".").filter(Boolean); + } + + function getByPath(source, path) { + var parts = parsePath(path); + var current = source; + for (var i = 0; i < parts.length; i += 1) { + if (!current || typeof current !== "object") return undefined; + current = current[parts[i]]; + } + return current; + } + + function setByPath(source, path, value) { + var parts = parsePath(path); + var current = source; + for (var i = 0; i < parts.length - 1; i += 1) { + var key = parts[i]; + if (!current[key] || typeof current[key] !== "object" || Array.isArray(current[key])) { + current[key] = {}; + } + current = current[key]; + } + if (parts.length) current[parts[parts.length - 1]] = value; + } + + function stripLeadingEmoji(value) { + return String(value || "").replace(/^[\s\u2600-\u27BF\uD800-\uDBFF][\s\uFE0F\u200D\uDC00-\uDFFF]*/g, "").trim() || String(value || ""); + } + + function setBusy(key, value) { + state.busy[key] = Boolean(value); + render(); + } + + function setError(message) { + state.error = message || ""; + render(); + } + + function formatDuration(totalSeconds) { + var seconds = Math.max(0, Math.floor(Number(totalSeconds) || 0)); + var days = Math.floor(seconds / 86400); + var hours = Math.floor((seconds % 86400) / 3600); + var minutes = Math.floor((seconds % 3600) / 60); + var secs = seconds % 60; + var parts = []; + if (days) parts.push(days + "天"); + if (hours) parts.push(hours + "小时"); + if (minutes) parts.push(minutes + "分"); + if (secs || !parts.length) parts.push(secs + "秒"); + return parts.slice(0, 3).join(""); + } + + function formatDate(value) { + if (!value) return "--"; + var date = new Date(value); + if (isNaN(date.getTime())) return text(value); + return date.toLocaleString("zh-CN", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit" + }); + } + + function timestampMs(value) { + if (value === null || value === undefined || value === "") return null; + if (typeof value === "number") { + return value > 100000000000 ? value : value * 1000; + } + var raw = String(value); + if (/^\d+(\.\d+)?$/.test(raw)) { + var numeric = Number(raw); + return numeric > 100000000000 ? numeric : numeric * 1000; + } + var date = new Date(raw); + return isNaN(date.getTime()) ? null : date.getTime(); + } + + function clampPercent(value) { + var number = Math.round(Number(value) || 0); + return Math.max(0, Math.min(100, number)); + } + + function stableJson(value) { + if (Array.isArray(value)) { + return "[" + value.map(stableJson).join(",") + "]"; + } + if (value && typeof value === "object") { + var keys = Object.keys(value).sort(); + var parts = []; + for (var i = 0; i < keys.length; i += 1) { + parts.push(JSON.stringify(keys[i]) + ":" + stableJson(value[keys[i]])); + } + return "{" + parts.join(",") + "}"; + } + return JSON.stringify(value); + } + + function sameConfigValue(left, right) { + return stableJson(left || {}) === stableJson(right || {}); } function normalizePayload(payload) { - if (!payload || typeof payload !== "object") return {}; - if (payload.ok === true && payload.data && typeof payload.data === "object") { - return payload.data; + if (!payload || typeof payload !== "object") return payload || {}; + if (payload.error) { + throw new Error(typeof payload.error === "string" ? payload.error : payload.error.message || "请求失败"); + } + if (payload.ok === false || payload.success === false) { + throw new Error(payload.message || "请求失败"); } - if (payload.success === true && payload.data && typeof payload.data === "object") { - return payload.data; + if (typeof payload.code === "number" && payload.code !== 0) { + throw new Error(payload.message || payload.msg || "请求失败 (" + payload.code + ")"); + } + if ((payload.ok === true || payload.success === true || payload.code === 0) && Object.prototype.hasOwnProperty.call(payload, "data")) { + return normalizePayload(payload.data || {}); } return payload; } @@ -45,92 +227,1237 @@ clearInterval(timer); resolve(null); } - }, 80); + }, 60); }); } - function deriveReachableUrl(webAdmin) { - webAdmin = webAdmin || {}; - var port = Number(webAdmin.port || 4100); - if (webAdmin.bind_all && window.location && window.location.hostname) { - return window.location.protocol + "//" + window.location.hostname + ":" + port + "/"; + function apiGet(endpoint) { + if (!state.bridge || typeof state.bridge.apiGet !== "function") { + return Promise.reject(new Error("AstrBot Pages bridge 未注入,请从 AstrBot WebUI 的插件页面重新打开。")); + } + return Promise.resolve(state.bridge.apiGet(endpoint)).then(normalizePayload); + } + + function apiPost(endpoint, body) { + if (!state.bridge || typeof state.bridge.apiPost !== "function") { + return Promise.reject(new Error("AstrBot Pages bridge 未注入,请从 AstrBot WebUI 的插件页面重新打开。")); + } + return Promise.resolve(state.bridge.apiPost(endpoint, body || {})).then(normalizePayload); + } + + function directBridgePost(endpoint, body) { + if (!state.bridge || typeof state.bridge.apiPost !== "function") { + return Promise.reject(new Error("AstrBot Pages bridge 未注入,请从 AstrBot WebUI 的插件页面重新打开。")); + } + return Promise.resolve(state.bridge.apiPost(endpoint, body || {})).then(normalizePayload); + } + + function route(endpoint) { + return endpoint; + } + + function hideBoot() { + var boot = $("loading-skeleton"); + if (!boot) return; + boot.classList.add("is-exiting"); + setTimeout(function () { + if (boot.parentNode) boot.parentNode.removeChild(boot); + }, 220); + } + + function initTheme() { + document.body.classList.toggle("dark-theme", state.theme === "dark"); + document.documentElement.classList.toggle("theme-dark", state.theme === "dark"); + } + + function toggleTheme() { + state.theme = state.theme === "dark" ? "light" : "dark"; + safeStorageSet("theme", state.theme); + initTheme(); + renderHeader(); + } + + function navButton(key) { + var meta = viewMeta[key]; + return [ + '' + ].join(""); + } + + function shellHtml() { + return [ + '

' + ].join(""); + } + + function renderShell() { + $("root").innerHTML = shellHtml(); + bindShellEvents(); + render(); + } + + function bindShellEvents() { + document.addEventListener("click", function (event) { + var viewBtn = closest(event.target, "[data-view]"); + if (viewBtn) { + switchView(viewBtn.getAttribute("data-view")); + return; + } + var action = closest(event.target, "[data-action]"); + if (!action) return; + handleAction(action.getAttribute("data-action"), action); + }); + document.addEventListener("change", function (event) { + if (event.target && event.target.id === "session-select") { + state.selectedSession = event.target.value; + state.expandedKeys = []; + loadSessionDetail(state.selectedSession); + } + if (event.target && event.target.id === "doc-select") { + state.selectedMarkdownPath = event.target.value; + loadMarkdownDocument(state.selectedMarkdownPath); + } + if (event.target && event.target.getAttribute("data-config-path")) { + updateConfigFromControl(event.target, true); + } + }); + document.addEventListener("input", function (event) { + if (event.target && event.target.getAttribute("data-config-path")) { + updateConfigFromControl(event.target, false); + } + }); + } + + function handleAction(action, node) { + if (action === "refresh") loadCurrentView(); + if (action === "theme") toggleTheme(); + if (action === "open-dir") openDirectory(node.getAttribute("data-path")); + if (action === "trigger-job") triggerJob(node.getAttribute("data-id")); + if (action === "reschedule-job") rescheduleJob(node.getAttribute("data-id")); + if (action === "cancel-job") cancelJob(node.getAttribute("data-id")); + if (action === "read-notification") readNotification(node.getAttribute("data-id")); + if (action === "read-all-notifications") readAllNotifications(); + if (action === "refresh-notifications") refreshNotifications(); + if (action === "save-config") saveConfig(); + if (action === "load-config") loadConfig(); + if (action === "save-session") saveSessionConfig(); + if (action === "reset-session") resetSessionConfig(); + if (action === "config-mode") switchConfigMode(node.getAttribute("data-mode")); + if (action === "toggle-config") toggleConfigGroup(node.getAttribute("data-path")); + if (action === "toggle-all-config") toggleAllConfigGroups(); + if (action === "reset-config-defaults") resetConfigDefaults(); + if (action === "discard-config") loadConfig(); + } + + function switchView(view) { + if (!viewMeta[view]) return; + state.view = view; + render(); + loadCurrentView(); + } + + function loadCurrentView() { + if (state.view === "status") loadDashboard(); + if (state.view === "tasks") loadJobs(); + if (state.view === "notifications") loadNotifications(); + if (state.view === "docs") loadMarkdownFiles(); + if (state.view === "config") loadConfig(); + } + + function renderHeader() { + var meta = viewMeta[state.view]; + var status = state.status || {}; + var connected = state.bridgeReady; + $("pc-topbar").innerHTML = [ + '
', escapeHtml(meta.label), '
', + '
', escapeHtml(meta.subtitle), '
', + '
', + '🕒 ', escapeHtml(formatDate(new Date())), '', + '', connected ? "已连接 Pages bridge" : "未连接 Pages bridge", '', + '同步 ', state.lastRealtimeAt ? escapeHtml(formatDate(state.lastRealtimeAt)) : '--', '', + 'WebSocket ', Number(status.ws_connections || 0), ' 个', + '', + '', + '
' + ].join(""); + } + + function renderError() { + $("pc-error").innerHTML = state.error ? '
' + escapeHtml(state.error) + '
' : ""; + } + + function render() { + if (!$("pc-topbar")) return; + initTheme(); + renderHeader(); + renderError(); + var keys = ["status", "tasks", "notifications", "docs", "config"]; + for (var i = 0; i < keys.length; i += 1) { + var section = $("view-" + keys[i]); + if (section) section.classList.toggle("is-active", state.view === keys[i]); + } + var navs = document.querySelectorAll("[data-view]"); + for (var n = 0; n < navs.length; n += 1) { + navs[n].classList.toggle("is-active", navs[n].getAttribute("data-view") === state.view); + } + renderStatus(); + renderTasks(); + renderNotifications(); + renderDocs(); + renderConfig(); + } + + function metric(label, value, hint) { + return [ + '
', + '
', escapeHtml(label), '
', + '
', escapeHtml(value), '
', + '
', escapeHtml(hint || ""), '
', + '
' + ].join(""); + } + + function renderStatus() { + var status = state.status || {}; + var timerCards = collectTimerCards(status); + var autoCards = timerCards.auto; + var groupCards = timerCards.group; + var fallbackCards = timerCards.fallback; + var scheduledAutoCards = fallbackCards.filter(function (card) { + return detectSessionType(card.session_id || card.session || card.id) !== "group"; + }); + var scheduledGroupCards = fallbackCards.filter(function (card) { + return detectSessionType(card.session_id || card.session || card.id) === "group"; + }); + var visibleAutoTriggerCount = autoCards.length + scheduledAutoCards.length; + var visibleGroupTimerCount = groupCards.length + scheduledGroupCards.length; + var autoTriggerSummary = visibleAutoTriggerCount + " 个"; + if (scheduledAutoCards.length) autoTriggerSummary += "(实时 " + autoCards.length + " / 调度 " + scheduledAutoCards.length + ")"; + var groupTimerSummary = visibleGroupTimerCount + " 个"; + if (scheduledGroupCards.length) groupTimerSummary += "(实时 " + groupCards.length + " / 调度 " + scheduledGroupCards.length + ")"; + var allCards = groupCards.concat(autoCards).concat(fallbackCards); + var realJobsCount = Number(status.jobs_count || 0); + var visibleJobsCount = Math.max(realJobsCount, asArray(state.jobs).length); + var pendingJobsCount = Math.max(0, visibleJobsCount - realJobsCount); + $("view-status").innerHTML = [ + '
', + metric("插件状态", status.running ? "运行中" : "已停止", "版本 " + text(status.version, "...")), + metric("运行时长", formatDuration(status.uptime_seconds), "启动后持续运行时间"), + metric("调度任务", text(visibleJobsCount, "0"), (status.scheduler_running ? "调度器运行中" : "调度器未启动") + " · 已调度 " + realJobsCount + " / 待调度 " + pendingJobsCount), + metric("会话数据", text(status.sessions_count, "0"), "自动/会话触发 " + visibleAutoTriggerCount + " / 群沉默 " + visibleGroupTimerCount), + '
', + '
', + '
会话计时器可视化
实时展示自动触发检测与群沉默检测的倒计时、进度和会话状态。
', + renderTimerList(allCards), '
', + '
调度概览
', + '
', + infoRow("调度器", status.scheduler_running ? "运行中" : "未启动"), + infoRow("当前任务总数", visibleJobsCount + " 个"), + pendingJobsCount ? infoRow("待调度会话", pendingJobsCount + " 个") : "", + infoRow("自动/会话触发计时器", autoTriggerSummary), + infoRow("群沉默计时器", groupTimerSummary), + fallbackCards.length ? infoRow("其中会话调度倒计时", fallbackCards.length + " 个") : "", + infoRow("数据时间", formatDate(status.timestamp)), + '
' + ].join(""); + } + + function infoRow(label, value) { + return '
' + escapeHtml(label) + '
' + escapeHtml(value) + '
'; + } + + function collectTimerCards(status) { + var autoCards = asArray(status.auto_trigger_cards); + var groupCards = asArray(status.group_timer_cards); + var seen = {}; + var seenSessions = {}; + var fallbackSessions = {}; + var fallback = []; + var jobs = asArray(state.jobs); + var sessions = asArray(state.sessions); + + function cardSessionId(card) { + return String(card && (card.session_id || card.session || card.id) || ""); + } + + function cardHasTarget(card) { + return !!(card && (card.target_time || card.next_trigger_time || card.next_run_time || card.remaining_seconds !== null && card.remaining_seconds !== undefined)); + } + + function mark(card) { + var sessionId = cardSessionId(card); + var key = String(card.timer_kind || "timer") + ":" + sessionId; + if (key !== "timer:") seen[key] = true; + if (sessionId && cardHasTarget(card)) seenSessions[sessionId] = true; + } + for (var a = 0; a < autoCards.length; a += 1) mark(autoCards[a] || {}); + for (var g = 0; g < groupCards.length; g += 1) mark(groupCards[g] || {}); + + function pushFallback(source, kind) { + source = source || {}; + var id = source.session || source.id || source.session_id || ""; + var targetRaw = source.next_trigger_time || source.next_run_time; + var target = timestampMs(targetRaw); + if (!id || !target) return; + var key = kind + ":" + id; + if (seen[key] || seenSessions[id]) return; + var remaining = Math.max(0, Math.ceil((target - Date.now()) / 1000)); + var windowSeconds = Number(source.last_schedule_random_interval_seconds || source.last_schedule_max_interval_seconds || 0); + if (!windowSeconds && source.schedule_max_interval_minutes) windowSeconds = Number(source.schedule_max_interval_minutes) * 60; + var started = timestampMs(source.last_scheduled_at); + var progress = 0; + if (started && target > started) { + progress = clampPercent(((Date.now() - started) / (target - started)) * 100); + } else if (windowSeconds > 0) { + progress = clampPercent(((windowSeconds - remaining) / windowSeconds) * 100); + } + fallback.push({ + session_id: id, + session: id, + session_name: source.session_name, + session_display_name: source.session_display_name, + session_category: source.session_category, + timer_kind: kind, + timer_kind_label: kind === "scheduled_job" ? "调度任务" : "下次触发", + status: remaining <= 0 ? "expired" : "running", + remaining_seconds: remaining, + target_time: target / 1000, + window_seconds: windowSeconds, + progress_percent: progress, + unanswered_count: source.unanswered_count, + max_unanswered_times: source.max_unanswered_times, + last_schedule_strategy: source.last_schedule_strategy, + last_schedule_reason: source.last_schedule_reason, + last_schedule_rule: source.last_schedule_rule, + last_schedule_source: source.last_schedule_source + }); + seen[key] = true; + seenSessions[id] = true; + fallbackSessions[id] = true; + } + + for (var j = 0; j < jobs.length; j += 1) pushFallback(jobs[j], "scheduled_job"); + for (var s = 0; s < sessions.length; s += 1) pushFallback(sessions[s], "session_next_trigger"); + fallback.sort(function (left, right) { + return Number(left.remaining_seconds || 0) - Number(right.remaining_seconds || 0); + }); + function keepPrimaryCard(card) { + var id = cardSessionId(card); + return !id || !fallbackSessions[id] || cardHasTarget(card); + } + return { auto: autoCards.filter(keepPrimaryCard), group: groupCards.filter(keepPrimaryCard), fallback: fallback }; + } + + function timerStatusLabel(item) { + if (item.status === "waiting_message") return "等待消息"; + if (item.status === "waiting_idle") return "等待空闲"; + if (item.status === "pending_timer") return "未挂起"; + if (item.status === "unknown") return "待确认"; + if (item.remaining_seconds === null || item.remaining_seconds === undefined) return "未启动"; + if (Number(item.remaining_seconds || 0) <= 0) return "待刷新"; + if (Number(item.remaining_seconds || 0) <= 300) return "即将触发"; + return "计时中"; + } + + function scheduleStrategyLabel(item) { + var strategy = String(item && item.last_schedule_strategy || "").toLowerCase(); + var source = String(item && item.last_schedule_source || "").toLowerCase(); + if (strategy === "contextual" || source === "recent_context") return "语境预测"; + if (strategy === "random" || source === "random_interval") return "随机区间"; + return ""; + } + + function scheduleReasonLabel(item) { + var rule = String(item && item.last_schedule_rule || ""); + var reason = String(item && item.last_schedule_reason || ""); + var labels = { + explicit_delay: "明确延后", + tomorrow: "明天再聊", + do_not_disturb: "暂不打扰", + sleep_night: "睡眠休息", + movie: "观影追剧", + meeting_or_class: "会议课程", + commute: "通勤路上", + meal: "用餐时间", + shower: "短时离开", + game: "游戏中", + short_later: "稍后再聊" + }; + if (labels[rule]) return labels[rule]; + if (reason.indexOf("context:explicit_delay:") === 0) { + return "明确延后 " + reason.replace("context:explicit_delay:", ""); + } + return reason; + } + + function renderTimerList(items) { + if (!items.length) return '
🫧 暂无运行中的会话计时器
'; + var html = ['
']; + for (var i = 0; i < items.length; i += 1) { + var item = items[i] || {}; + var progress = clampPercent(item.progress_percent); + var hasCountdown = item.remaining_seconds !== null && item.remaining_seconds !== undefined; + var remaining = hasCountdown ? formatDuration(item.remaining_seconds) : (item.status === "waiting_message" ? "等待消息" : "--"); + var countdownSuffix = hasCountdown ? " 后触发" : (item.status === "waiting_message" ? " 后开始" : " 待启动"); + var targetText = item.target_time ? formatDate(Number(item.target_time) * 1000) : "--"; + var chipClass = hasCountdown && Number(item.remaining_seconds || 0) > 300 ? "is-ok" : "is-warn"; + var detailText = (item.timer_kind_label || item.title || item.timer_kind || "计时器") + " · 目标时间 " + targetText + " · 未回复 " + text(item.unanswered_count, "0") + "/" + text(item.max_unanswered_times, "0"); + var strategyText = scheduleStrategyLabel(item); + var reasonText = scheduleReasonLabel(item); + if (strategyText) detailText += " · 调度 " + strategyText; + if (reasonText && strategyText === "语境预测") detailText += " · " + reasonText; + if (item.inactive_reason) detailText += " · " + item.inactive_reason; + html.push('
'); + html.push('
', escapeHtml(item.session_display_name || item.session_name || item.session || item.session_id || item.id || "会话"), '
'); + html.push('
', escapeHtml(item.session_id || item.session || item.id || ""), '
'); + html.push('', escapeHtml(timerStatusLabel(item)), '
'); + html.push('
', escapeHtml(remaining), '', escapeHtml(countdownSuffix), '
'); + html.push('
', escapeHtml(detailText), '
'); + html.push('
'); + html.push('
进度 ', progress, '%', item.window_seconds ? ' · 窗口 ' + escapeHtml(formatDuration(item.window_seconds)) : '', '
'); + html.push('
'); + } + html.push('
'); + return html.join(""); + } + + function renderTasks() { + var jobs = state.jobs || []; + if (!jobs.length) { + $("view-tasks").innerHTML = '
任务列表
暂无调度任务或已配置会话
'; + return; + } + var html = ['
任务列表
当前共 ', jobs.length, ' 个任务
']; + for (var i = 0; i < jobs.length; i += 1) { + var job = jobs[i] || {}; + var id = job.id || job.session || ""; + var isPending = job.status === "pending_schedule"; + var statusLabel = job.status_label || (isPending ? "待调度" : "已调度"); + var nextTime = job.next_run_time || job.next_trigger_time; + var detailText = "下次运行: " + formatDate(timestampMs(nextTime) || nextTime) + " · 来源: " + text(job.source_mode, "--") + " · 未回复: " + text(job.unanswered_count, "0") + "/" + text(job.max_unanswered_times, "0"); + var strategyText = scheduleStrategyLabel(job); + var reasonText = scheduleReasonLabel(job); + if (strategyText) detailText += " · 调度: " + strategyText; + if (reasonText && strategyText === "语境预测") detailText += " · " + reasonText; + if (job.inactive_reason) detailText += " · " + job.inactive_reason; + html.push('
'); + html.push('
', escapeHtml(job.session_display_name || job.session_name || id || "任务"), ' ', escapeHtml(statusLabel), '
'); + html.push('
UMO: ', escapeHtml(id), '
'); + html.push('
', escapeHtml(detailText), '
'); + html.push('
'); + html.push(''); + html.push(''); + if (!isPending) html.push(''); + html.push('
'); + } + html.push('
'); + $("view-tasks").innerHTML = html.join(""); + } + + function renderNotifications() { + var items = state.notifications || []; + var meta = state.notificationsMeta || {}; + var html = ['
通知中心
未读 ', Number(meta.unread_count || 0), ' / 总数 ', Number(meta.total_count || items.length || 0), '
']; + if (!items.length) { + html.push('
暂无通知
'); + $("view-notifications").innerHTML = html.join(""); + return; + } + html.push('
'); + for (var i = 0; i < items.length; i += 1) { + var item = items[i] || {}; + var id = item.id == null ? "" : String(item.id); + html.push('
'); + html.push('
', escapeHtml(item.title || item.type || "通知"), '
'); + html.push('
', escapeHtml(item.level || item.severity || "info"), ' · ', escapeHtml(formatDate(item.created_at || item.timestamp || item.time)), '
'); + html.push('
', escapeHtml(item.content || item.message || item.text || ""), '
'); + if (id) html.push('
'); + html.push('
'); + } + html.push('