diff --git a/README.md b/README.md index ac8987d..81f53f5 100644 --- a/README.md +++ b/README.md @@ -29,3 +29,8 @@ docker compose up --build - 默认会以 `--reload` 模式启动,监听本地 8080 端口,可在浏览器访问 `http://localhost:8080`。 - 通过 `./app/config.json` 保存的 Plex 配置信息会在主机和容器间共享,便于调试时保留登录 token 等数据。 - 如需自定义端口或其他参数,可在 `docker-compose.yml` 中调整。 + +## 前端构建与配置 + +- `sample-front-end` 中提供了新的 React UI,运行 `npm install && npm run build` 会将打包产物输出到 `app/static/frontend`,API 直接调用 FastAPI 后端。 +- 环境变量 `STATUS_CHECK_INTERVAL_SECONDS` 用于控制前端自动刷新云端连接状态的轮询间隔(默认 45 秒,最小 15 秒),用于避免频繁请求导致的循环异常。 diff --git a/app/config.json b/app/config.json index cecb71c..cd12e49 100644 --- a/app/config.json +++ b/app/config.json @@ -2,7 +2,10 @@ "theme": "auto", "token": "", "server_url": "", - "server_port": "", - "server_scheme": "", - "path_rules": [] + "server_port": "32400", + "server_scheme": "https", + "library_name": "", + "path_rules": [], + "local_playlist_dir": "playlist", + "sync_strategy": "LOCAL_OVERWRITE" } \ No newline at end of file diff --git a/app/main.py b/app/main.py index 00234a0..56e6cc4 100644 --- a/app/main.py +++ b/app/main.py @@ -1,26 +1,38 @@ import os -from app.utils.config import server_config -from app.utils.playlist_merge import SyncMode, sync_all_playlists, TEST_PLAYLIST_DIR -from fastapi import FastAPI, Request, Form -from fastapi.responses import HTMLResponse, RedirectResponse -from fastapi.templating import Jinja2Templates +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI, Form, Request +from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse from fastapi.staticfiles import StaticFiles -from app.utils.plex_client import plex_client +from fastapi.templating import Jinja2Templates +from pydantic import BaseModel, ConfigDict, Field + +from app.utils.config import server_config from app.utils.logger import logger from app.utils.local_playlist import scan_local_playlists +from app.utils.playlist_merge import SyncMode, TEST_PLAYLIST_DIR, sync_all_playlists +from app.utils.plex_client import MUSIC_LIBRARY_TYPE, plex_client + +BASE_DIR = Path(__file__).resolve().parent +FRONTEND_DIST = BASE_DIR / "static" / "frontend" +FRONTEND_INDEX = FRONTEND_DIST / "index.html" +DEFAULT_STATUS_INTERVAL = max( + 15, int(os.getenv("STATUS_CHECK_INTERVAL_SECONDS", "45")) +) app = FastAPI() -templates = Jinja2Templates( - directory=os.path.join(os.path.dirname(__file__), "templates") -) +templates = Jinja2Templates(directory=BASE_DIR / "templates") # mount static files -# 这里的路径是相对于 main.py 文件所在的目录 -app.mount( - "/static", - StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), - name="static", -) +app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") + +if (FRONTEND_DIST / "assets").exists(): + app.mount( + "/assets", + StaticFiles(directory=FRONTEND_DIST / "assets"), + name="assets", + ) SYNC_MODE_OPTIONS = [ @@ -47,7 +59,70 @@ SYNC_MODE_OPTIONS = [ ] -def _get_cloud_playlists() -> tuple[list[dict], str, dict, str, list[str]]: +class PlexConnectionSettings(BaseModel): + protocol: Literal["http", "https"] = Field("https", description="Connection protocol") + address: str = Field(..., description="Plex server domain or IP") + port: str = Field("32400", description="Plex server port") + token: str = "" + username: str | None = "" + password: str | None = "" + library: str | None = None + + +class RegexRule(BaseModel): + pattern: str + replacement: str = "" + model_config = ConfigDict(extra="ignore") + + +class StrategyPayload(BaseModel): + strategy: str + + +class LibrarySelection(BaseModel): + library: str + + +def _format_regex_rules(rules: list[dict]) -> list[dict]: + formatted: list[dict] = [] + for idx, rule in enumerate(rules or []): + pattern = (rule.get("pattern") or "").strip() + replacement = rule.get("replacement") or "" + if not pattern: + continue + formatted.append( + { + "id": f"rule-{idx}", + "pattern": pattern, + "replacement": replacement, + } + ) + return formatted + + +def _list_music_libraries() -> list[dict]: + libraries: list[dict] = [] + if not plex_client.connected or not plex_client.server: + return libraries + try: + for lib in plex_client.server.library.sections(): + if getattr(lib, "type", None) != MUSIC_LIBRARY_TYPE: + continue + libraries.append( + { + "id": getattr(lib, "uuid", getattr(lib, "key", lib.title)), + "title": lib.title, + "type": getattr(lib, "type", ""), + } + ) + except Exception as exc: + logger.warning(f"Unable to fetch music library metadata: {exc}") + return libraries + + +def _get_cloud_playlists( + include_playlists: bool = True, +) -> tuple[list[dict], str, dict, str, list[str]]: """Fetch playlists and connection state from the remote Plex server.""" server_config.load() @@ -89,24 +164,30 @@ def _get_cloud_playlists() -> tuple[list[dict], str, dict, str, list[str]]: selected_library = music_libraries[0] server_config.set_and_save_config(library_name=selected_library) - for playlist in plex_client.get_lib_playlists(selected_library) or []: - track_count = getattr(playlist, "itemCount", None) - if track_count is None: - try: - track_count = len(playlist.items()) - except Exception: - track_count = 0 - playlists.append( - { - "name": playlist.title, - "track_count": track_count, - } - ) + if include_playlists: + for playlist in plex_client.get_lib_playlists(selected_library) or []: + track_count = getattr(playlist, "itemCount", None) + if track_count is None: + try: + track_count = len(playlist.items()) + except Exception: + track_count = 0 + playlists.append( + { + "name": playlist.title, + "track_count": track_count, + "rating_key": getattr(playlist, "ratingKey", playlist.title), + "last_modified": getattr( + getattr(playlist, "updatedAt", None), "isoformat", lambda: None + )(), + } + ) except Exception as exc: logger.warning(f"Failed to fetch cloud playlists: {exc}") status = "failed" - playlists.sort(key=lambda item: item["name"].lower()) + if include_playlists: + playlists.sort(key=lambda item: item["name"].lower()) return playlists, status, server_info, selected_library, music_libraries @@ -118,6 +199,7 @@ def _build_home_context( sync_result: dict | None = None, selected_mode: str | None = None, ): + local_path = local_path or server_config.local_playlist_dir server_config.load() local_playlists = scan_local_playlists(local_path) ( @@ -148,16 +230,203 @@ def _build_home_context( } -# 显示主页 -@app.get("/", response_class=HTMLResponse) -async def home(request: Request, local_path: str = "playlist"): - context = _build_home_context(request, local_path) +def _format_local_playlists_response(playlists: list[dict]) -> list[dict]: + formatted: list[dict] = [] + for item in playlists: + formatted.append( + { + "id": f"local::{item.get('name', '')}", + "title": item.get("name", ""), + "trackCount": item.get("track_count", 0), + "lastUpdated": item.get("last_modified"), + "path": item.get("path"), + } + ) + return formatted + +def _format_cloud_playlists_response(playlists: list[dict]) -> list[dict]: + formatted: list[dict] = [] + for item in playlists: + formatted.append( + { + "id": str(item.get("rating_key") or item.get("name")), + "title": item.get("name", ""), + "trackCount": item.get("track_count", 0), + "lastUpdated": item.get("last_modified"), + } + ) + return formatted + + +# 显示前端(React 构建后的静态文件),若缺失则回退到旧版模板 +@app.get("/", response_class=HTMLResponse) +async def serve_frontend(request: Request, local_path: str | None = None): + if FRONTEND_INDEX.exists(): + return FileResponse(FRONTEND_INDEX) + + context = _build_home_context( + request, + local_path or server_config.local_playlist_dir, + message="前端静态文件未构建,已回退到旧版页面。", + message_type="warning", + ) return templates.TemplateResponse("home.html", context) +@app.get("/api/ui-config") +async def get_ui_config(): + """Expose UI-related settings for the front-end.""" + + server_config.load() + return { + "status": "success", + "data": { + "statusCheckIntervalSeconds": DEFAULT_STATUS_INTERVAL, + "localPlaylistDir": server_config.local_playlist_dir, + }, + } + + +@app.get("/api/settings") +async def get_settings(): + server_config.load() + return { + "status": "success", + "data": { + "syncStrategy": server_config.sync_strategy, + "regexRules": _format_regex_rules(server_config.path_rules), + }, + } + + +@app.post("/api/settings/strategy") +async def save_strategy(payload: StrategyPayload): + server_config.set_and_save_config(sync_strategy=payload.strategy) + return {"status": "success", "data": {"syncStrategy": payload.strategy}} + + +@app.get("/api/regex-rules") +async def get_regex_rules(): + server_config.load() + return {"status": "success", "data": _format_regex_rules(server_config.path_rules)} + + +@app.post("/api/regex-rules") +async def save_regex_rules(rules: list[RegexRule]): + cleaned = [ + {"pattern": rule.pattern.strip(), "replacement": rule.replacement or ""} + for rule in rules + if rule.pattern.strip() + ] + server_config.set_and_save_config(path_rules=cleaned) + return {"status": "success", "data": _format_regex_rules(cleaned)} + + +@app.get("/api/playlists/{server_type}") +async def api_get_playlists( + server_type: Literal["local", "cloud"], local_path: str | None = None +): + if server_type == "local": + path = local_path or server_config.local_playlist_dir + server_config.set_and_save_config(local_playlist_dir=path) + playlists = scan_local_playlists(path) + return {"status": "success", "data": _format_local_playlists_response(playlists)} + + playlists, status, _, _, _ = _get_cloud_playlists() + if status != "connected": + return { + "status": "error", + "message": "未连接到云端服务器,无法获取播放列表。", + "data": [], + } + return {"status": "success", "data": _format_cloud_playlists_response(playlists)} + + +@app.get("/api/server/status") +async def api_server_status(): + _, status, server_info, selected_library, _ = _get_cloud_playlists( + include_playlists=False + ) + is_connected = status == "connected" + libraries = _list_music_libraries() if is_connected else [] + return { + "status": "success", + "data": { + "isConnected": is_connected, + "name": server_info.get("name"), + "ip": server_config.url or None, + "port": int(server_config.port) if server_config.port else None, + "libraryName": selected_library or None, + "libraries": libraries, + }, + } + + +@app.post("/api/server/connect") +async def api_server_connect(payload: PlexConnectionSettings): + try: + _, token_success = plex_client.connect( + username=payload.username or "", + password=payload.password or "", + token=payload.token or "", + scheme=payload.protocol, + url=payload.address, + port=payload.port, + ) + except Exception as exc: + return JSONResponse( + { + "status": "error", + "message": f"连接失败:{exc}", + "data": {"token": "", "serverInfo": {"isConnected": False}}, + }, + status_code=400, + ) + + library_names = plex_client.get_libs_name_list() or [] + selected_library = payload.library if payload.library in library_names else "" + if not selected_library and library_names: + selected_library = library_names[0] + + libraries = _list_music_libraries() + + server_config.set_and_save_config( + token=token_success, + scheme=payload.protocol, + url=payload.address, + port=payload.port, + library_name=selected_library, + ) + + return { + "status": "success", + "message": "连接成功", + "data": { + "token": token_success, + "serverInfo": { + "isConnected": True, + "name": getattr(plex_client.server, "friendlyName", payload.address), + "ip": payload.address, + "port": int(payload.port) if payload.port else None, + "libraryName": selected_library, + "libraries": libraries, + }, + }, + } + + +@app.post("/api/server/library") +async def api_select_library(selection: LibrarySelection): + server_config.set_and_save_config(library_name=selection.library) + return {"status": "success", "data": {"libraryName": selection.library}} + + @app.post("/sync", response_class=HTMLResponse) -async def trigger_sync(request: Request, mode: str = Form(...), local_path: str = Form("playlist")): +async def trigger_sync( + request: Request, mode: str = Form(...), local_path: str = Form(None) +): + local_path = local_path or server_config.local_playlist_dir try: sync_mode = SyncMode(mode) except ValueError: @@ -214,10 +483,11 @@ async def trigger_sync(request: Request, mode: str = Form(...), local_path: str @app.post("/path-rules", response_class=HTMLResponse) async def save_path_rules( request: Request, - local_path: str = Form("playlist"), + local_path: str = Form(None), pattern: list[str] | None = Form(None), replacement: list[str] | None = Form(None), ): + local_path = local_path or server_config.local_playlist_dir patterns = pattern or [] replacements = replacement or [] diff --git a/app/static/frontend/assets/index-C8nziRPz.js b/app/static/frontend/assets/index-C8nziRPz.js new file mode 100644 index 0000000..72b1dbb --- /dev/null +++ b/app/static/frontend/assets/index-C8nziRPz.js @@ -0,0 +1,236 @@ +(function(){const _=document.createElement("link").relList;if(_&&_.supports&&_.supports("modulepreload"))return;for(const M of document.querySelectorAll('link[rel="modulepreload"]'))y(M);new MutationObserver(M=>{for(const B of M)if(B.type==="childList")for(const F of B.addedNodes)F.tagName==="LINK"&&F.rel==="modulepreload"&&y(F)}).observe(document,{childList:!0,subtree:!0});function H(M){const B={};return M.integrity&&(B.integrity=M.integrity),M.referrerPolicy&&(B.referrerPolicy=M.referrerPolicy),M.crossOrigin==="use-credentials"?B.credentials="include":M.crossOrigin==="anonymous"?B.credentials="omit":B.credentials="same-origin",B}function y(M){if(M.ep)return;M.ep=!0;const B=H(M);fetch(M.href,B)}})();function Y0(S){return S&&S.__esModule&&Object.prototype.hasOwnProperty.call(S,"default")?S.default:S}var rf={exports:{}},Au={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var z0;function dm(){if(z0)return Au;z0=1;var S=Symbol.for("react.transitional.element"),_=Symbol.for("react.fragment");function H(y,M,B){var F=null;if(B!==void 0&&(F=""+B),M.key!==void 0&&(F=""+M.key),"key"in M){B={};for(var ll in M)ll!=="key"&&(B[ll]=M[ll])}else B=M;return M=B.ref,{$$typeof:S,type:y,key:F,ref:M!==void 0?M:null,props:B}}return Au.Fragment=_,Au.jsx=H,Au.jsxs=H,Au}var E0;function ym(){return E0||(E0=1,rf.exports=dm()),rf.exports}var d=ym(),df={exports:{}},Q={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var T0;function mm(){if(T0)return Q;T0=1;var S=Symbol.for("react.transitional.element"),_=Symbol.for("react.portal"),H=Symbol.for("react.fragment"),y=Symbol.for("react.strict_mode"),M=Symbol.for("react.profiler"),B=Symbol.for("react.consumer"),F=Symbol.for("react.context"),ll=Symbol.for("react.forward_ref"),O=Symbol.for("react.suspense"),z=Symbol.for("react.memo"),X=Symbol.for("react.lazy"),C=Symbol.for("react.activity"),tl=Symbol.iterator;function El(o){return o===null||typeof o!="object"?null:(o=tl&&o[tl]||o["@@iterator"],typeof o=="function"?o:null)}var _l={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},xl=Object.assign,$l={};function Nl(o,T,N){this.props=o,this.context=T,this.refs=$l,this.updater=N||_l}Nl.prototype.isReactComponent={},Nl.prototype.setState=function(o,T){if(typeof o!="object"&&typeof o!="function"&&o!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,o,T,"setState")},Nl.prototype.forceUpdate=function(o){this.updater.enqueueForceUpdate(this,o,"forceUpdate")};function q(){}q.prototype=Nl.prototype;function K(o,T,N){this.props=o,this.context=T,this.refs=$l,this.updater=N||_l}var nl=K.prototype=new q;nl.constructor=K,xl(nl,Nl.prototype),nl.isPureReactComponent=!0;var Ql=Array.isArray;function bl(){}var L={H:null,A:null,T:null,S:null},fl=Object.prototype.hasOwnProperty;function Tl(o,T,N){var U=N.ref;return{$$typeof:S,type:o,key:T,ref:U!==void 0?U:null,props:N}}function Dl(o,T){return Tl(o.type,T,o.props)}function Al(o){return typeof o=="object"&&o!==null&&o.$$typeof===S}function Ol(o){var T={"=":"=0",":":"=2"};return"$"+o.replace(/[=:]/g,function(N){return T[N]})}var Ht=/\/+/g;function _t(o,T){return typeof o=="object"&&o!==null&&o.key!=null?Ol(""+o.key):T.toString(36)}function Wl(o){switch(o.status){case"fulfilled":return o.value;case"rejected":throw o.reason;default:switch(typeof o.status=="string"?o.then(bl,bl):(o.status="pending",o.then(function(T){o.status==="pending"&&(o.status="fulfilled",o.value=T)},function(T){o.status==="pending"&&(o.status="rejected",o.reason=T)})),o.status){case"fulfilled":return o.value;case"rejected":throw o.reason}}throw o}function m(o,T,N,U,Z){var k=typeof o;(k==="undefined"||k==="boolean")&&(o=null);var ol=!1;if(o===null)ol=!0;else switch(k){case"bigint":case"string":case"number":ol=!0;break;case"object":switch(o.$$typeof){case S:case _:ol=!0;break;case X:return ol=o._init,m(ol(o._payload),T,N,U,Z)}}if(ol)return Z=Z(o),ol=U===""?"."+_t(o,0):U,Ql(Z)?(N="",ol!=null&&(N=ol.replace(Ht,"$&/")+"/"),m(Z,T,N,"",function(Ca){return Ca})):Z!=null&&(Al(Z)&&(Z=Dl(Z,N+(Z.key==null||o&&o.key===Z.key?"":(""+Z.key).replace(Ht,"$&/")+"/")+ol)),T.push(Z)),1;ol=0;var Fl=U===""?".":U+":";if(Ql(o))for(var Cl=0;Cl>>1,el=m[ul];if(0>>1;ulM(N,D))UM(Z,N)?(m[ul]=Z,m[U]=D,ul=U):(m[ul]=N,m[T]=D,ul=T);else if(UM(Z,D))m[ul]=Z,m[U]=D,ul=U;else break l}}return A}function M(m,A){var D=m.sortIndex-A.sortIndex;return D!==0?D:m.id-A.id}if(S.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var B=performance;S.unstable_now=function(){return B.now()}}else{var F=Date,ll=F.now();S.unstable_now=function(){return F.now()-ll}}var O=[],z=[],X=1,C=null,tl=3,El=!1,_l=!1,xl=!1,$l=!1,Nl=typeof setTimeout=="function"?setTimeout:null,q=typeof clearTimeout=="function"?clearTimeout:null,K=typeof setImmediate<"u"?setImmediate:null;function nl(m){for(var A=H(z);A!==null;){if(A.callback===null)y(z);else if(A.startTime<=m)y(z),A.sortIndex=A.expirationTime,_(O,A);else break;A=H(z)}}function Ql(m){if(xl=!1,nl(m),!_l)if(H(O)!==null)_l=!0,bl||(bl=!0,Ol());else{var A=H(z);A!==null&&Wl(Ql,A.startTime-m)}}var bl=!1,L=-1,fl=5,Tl=-1;function Dl(){return $l?!0:!(S.unstable_now()-Tlm&&Dl());){var ul=C.callback;if(typeof ul=="function"){C.callback=null,tl=C.priorityLevel;var el=ul(C.expirationTime<=m);if(m=S.unstable_now(),typeof el=="function"){C.callback=el,nl(m),A=!0;break t}C===H(O)&&y(O),nl(m)}else y(O);C=H(O)}if(C!==null)A=!0;else{var o=H(z);o!==null&&Wl(Ql,o.startTime-m),A=!1}}break l}finally{C=null,tl=D,El=!1}A=void 0}}finally{A?Ol():bl=!1}}}var Ol;if(typeof K=="function")Ol=function(){K(Al)};else if(typeof MessageChannel<"u"){var Ht=new MessageChannel,_t=Ht.port2;Ht.port1.onmessage=Al,Ol=function(){_t.postMessage(null)}}else Ol=function(){Nl(Al,0)};function Wl(m,A){L=Nl(function(){m(S.unstable_now())},A)}S.unstable_IdlePriority=5,S.unstable_ImmediatePriority=1,S.unstable_LowPriority=4,S.unstable_NormalPriority=3,S.unstable_Profiling=null,S.unstable_UserBlockingPriority=2,S.unstable_cancelCallback=function(m){m.callback=null},S.unstable_forceFrameRate=function(m){0>m||125ul?(m.sortIndex=D,_(z,m),H(O)===null&&m===H(z)&&(xl?(q(L),L=-1):xl=!0,Wl(Ql,D-ul))):(m.sortIndex=el,_(O,m),_l||El||(_l=!0,bl||(bl=!0,Ol()))),m},S.unstable_shouldYield=Dl,S.unstable_wrapCallback=function(m){var A=tl;return function(){var D=tl;tl=A;try{return m.apply(this,arguments)}finally{tl=D}}}})(hf)),hf}var N0;function gm(){return N0||(N0=1,mf.exports=vm()),mf.exports}var vf={exports:{}},kl={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var O0;function bm(){if(O0)return kl;O0=1;var S=bf();function _(O){var z="https://react.dev/errors/"+O;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(S)}catch(_){console.error(_)}}return S(),vf.exports=bm(),vf.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var j0;function pm(){if(j0)return _u;j0=1;var S=gm(),_=bf(),H=Sm();function y(l){var t="https://react.dev/errors/"+l;if(1el||(l.current=ul[el],ul[el]=null,el--)}function N(l,t){el++,ul[el]=l.current,l.current=t}var U=o(null),Z=o(null),k=o(null),ol=o(null);function Fl(l,t){switch(N(k,t),N(Z,l),N(U,null),t.nodeType){case 9:case 11:l=(l=t.documentElement)&&(l=l.namespaceURI)?wr(l):0;break;default:if(l=t.tagName,t=t.namespaceURI)t=wr(t),l=Kr(t,l);else switch(l){case"svg":l=1;break;case"math":l=2;break;default:l=0}}T(U),N(U,l)}function Cl(){T(U),T(Z),T(k)}function Ca(l){l.memoizedState!==null&&N(ol,l);var t=U.current,e=Kr(t,l.type);t!==e&&(N(Z,l),N(U,e))}function Nu(l){Z.current===l&&(T(U),T(Z)),ol.current===l&&(T(ol),xu._currentValue=D)}var Kn,pf;function Oe(l){if(Kn===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);Kn=t&&t[1]||"",pf=-1)":-1u||f[a]!==v[u]){var p=` +`+f[a].replace(" at new "," at ");return l.displayName&&p.includes("")&&(p=p.replace("",l.displayName)),p}while(1<=a&&0<=u);break}}}finally{Jn=!1,Error.prepareStackTrace=e}return(e=l?l.displayName||l.name:"")?Oe(e):""}function Z0(l,t){switch(l.tag){case 26:case 27:case 5:return Oe(l.type);case 16:return Oe("Lazy");case 13:return l.child!==t&&t!==null?Oe("Suspense Fallback"):Oe("Suspense");case 19:return Oe("SuspenseList");case 0:case 15:return kn(l.type,!1);case 11:return kn(l.type.render,!1);case 1:return kn(l.type,!0);case 31:return Oe("Activity");default:return""}}function xf(l){try{var t="",e=null;do t+=Z0(l,e),e=l,l=l.return;while(l);return t}catch(a){return` +Error generating stack: `+a.message+` +`+a.stack}}var $n=Object.prototype.hasOwnProperty,Wn=S.unstable_scheduleCallback,Fn=S.unstable_cancelCallback,V0=S.unstable_shouldYield,w0=S.unstable_requestPaint,ct=S.unstable_now,K0=S.unstable_getCurrentPriorityLevel,zf=S.unstable_ImmediatePriority,Ef=S.unstable_UserBlockingPriority,Ou=S.unstable_NormalPriority,J0=S.unstable_LowPriority,Tf=S.unstable_IdlePriority,k0=S.log,$0=S.unstable_setDisableYieldValue,Ua=null,it=null;function ae(l){if(typeof k0=="function"&&$0(l),it&&typeof it.setStrictMode=="function")try{it.setStrictMode(Ua,l)}catch{}}var ft=Math.clz32?Math.clz32:I0,W0=Math.log,F0=Math.LN2;function I0(l){return l>>>=0,l===0?32:31-(W0(l)/F0|0)|0}var Mu=256,ju=262144,Du=4194304;function Me(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&261888;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function Cu(l,t,e){var a=l.pendingLanes;if(a===0)return 0;var u=0,n=l.suspendedLanes,c=l.pingedLanes;l=l.warmLanes;var i=a&134217727;return i!==0?(a=i&~n,a!==0?u=Me(a):(c&=i,c!==0?u=Me(c):e||(e=i&~l,e!==0&&(u=Me(e))))):(i=a&~n,i!==0?u=Me(i):c!==0?u=Me(c):e||(e=a&~l,e!==0&&(u=Me(e)))),u===0?0:t!==0&&t!==u&&(t&n)===0&&(n=u&-u,e=t&-t,n>=e||n===32&&(e&4194048)!==0)?t:u}function Ra(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function P0(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Af(){var l=Du;return Du<<=1,(Du&62914560)===0&&(Du=4194304),l}function In(l){for(var t=[],e=0;31>e;e++)t.push(l);return t}function Ha(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function ld(l,t,e,a,u,n){var c=l.pendingLanes;l.pendingLanes=e,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=e,l.entangledLanes&=e,l.errorRecoveryDisabledLanes&=e,l.shellSuspendCounter=0;var i=l.entanglements,f=l.expirationTimes,v=l.hiddenUpdates;for(e=c&~e;0"u")return null;try{return l.activeElement||l.body}catch{return l.body}}var cd=/[\n"\\]/g;function gt(l){return l.replace(cd,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function uc(l,t,e,a,u,n,c,i){l.name="",c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"?l.type=c:l.removeAttribute("type"),t!=null?c==="number"?(t===0&&l.value===""||l.value!=t)&&(l.value=""+vt(t)):l.value!==""+vt(t)&&(l.value=""+vt(t)):c!=="submit"&&c!=="reset"||l.removeAttribute("value"),t!=null?nc(l,c,vt(t)):e!=null?nc(l,c,vt(e)):a!=null&&l.removeAttribute("value"),u==null&&n!=null&&(l.defaultChecked=!!n),u!=null&&(l.checked=u&&typeof u!="function"&&typeof u!="symbol"),i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"?l.name=""+vt(i):l.removeAttribute("name")}function Bf(l,t,e,a,u,n,c,i){if(n!=null&&typeof n!="function"&&typeof n!="symbol"&&typeof n!="boolean"&&(l.type=n),t!=null||e!=null){if(!(n!=="submit"&&n!=="reset"||t!=null)){ac(l);return}e=e!=null?""+vt(e):"",t=t!=null?""+vt(t):e,i||t===l.value||(l.value=t),l.defaultValue=t}a=a??u,a=typeof a!="function"&&typeof a!="symbol"&&!!a,l.checked=i?l.checked:!!a,l.defaultChecked=!!a,c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"&&(l.name=c),ac(l)}function nc(l,t,e){t==="number"&&Hu(l.ownerDocument)===l||l.defaultValue===""+e||(l.defaultValue=""+e)}function Pe(l,t,e,a){if(l=l.options,t){t={};for(var u=0;u"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),oc=!1;if(Bt)try{var La={};Object.defineProperty(La,"passive",{get:function(){oc=!0}}),window.addEventListener("test",La,La),window.removeEventListener("test",La,La)}catch{oc=!1}var ne=null,rc=null,Yu=null;function wf(){if(Yu)return Yu;var l,t=rc,e=t.length,a,u="value"in ne?ne.value:ne.textContent,n=u.length;for(l=0;l=Qa),Ff=" ",If=!1;function Pf(l,t){switch(l){case"keyup":return Rd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ls(l){return l=l.detail,typeof l=="object"&&"data"in l?l.data:null}var aa=!1;function qd(l,t){switch(l){case"compositionend":return ls(t);case"keypress":return t.which!==32?null:(If=!0,Ff);case"textInput":return l=t.data,l===Ff&&If?null:l;default:return null}}function Yd(l,t){if(aa)return l==="compositionend"||!vc&&Pf(l,t)?(l=wf(),Yu=rc=ne=null,aa=!1,l):null;switch(l){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:e,offset:t-l};l=a}l:{for(;e;){if(e.nextSibling){e=e.nextSibling;break l}e=e.parentNode}e=void 0}e=fs(e)}}function os(l,t){return l&&t?l===t?!0:l&&l.nodeType===3?!1:t&&t.nodeType===3?os(l,t.parentNode):"contains"in l?l.contains(t):l.compareDocumentPosition?!!(l.compareDocumentPosition(t)&16):!1:!1}function rs(l){l=l!=null&&l.ownerDocument!=null&&l.ownerDocument.defaultView!=null?l.ownerDocument.defaultView:window;for(var t=Hu(l.document);t instanceof l.HTMLIFrameElement;){try{var e=typeof t.contentWindow.location.href=="string"}catch{e=!1}if(e)l=t.contentWindow;else break;t=Hu(l.document)}return t}function Sc(l){var t=l&&l.nodeName&&l.nodeName.toLowerCase();return t&&(t==="input"&&(l.type==="text"||l.type==="search"||l.type==="tel"||l.type==="url"||l.type==="password")||t==="textarea"||l.contentEditable==="true")}var wd=Bt&&"documentMode"in document&&11>=document.documentMode,ua=null,pc=null,Ka=null,xc=!1;function ds(l,t,e){var a=e.window===e?e.document:e.nodeType===9?e:e.ownerDocument;xc||ua==null||ua!==Hu(a)||(a=ua,"selectionStart"in a&&Sc(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),Ka&&wa(Ka,a)||(Ka=a,a=Dn(pc,"onSelect"),0>=c,u-=c,jt=1<<32-ft(t)+u|e<w?(P=R,R=null):P=R.sibling;var il=g(r,R,h[w],x);if(il===null){R===null&&(R=P);break}l&&R&&il.alternate===null&&t(r,R),s=n(il,s,w),cl===null?Y=il:cl.sibling=il,cl=il,R=P}if(w===h.length)return e(r,R),al&&Gt(r,w),Y;if(R===null){for(;ww?(P=R,R=null):P=R.sibling;var Ne=g(r,R,il.value,x);if(Ne===null){R===null&&(R=P);break}l&&R&&Ne.alternate===null&&t(r,R),s=n(Ne,s,w),cl===null?Y=Ne:cl.sibling=Ne,cl=Ne,R=P}if(il.done)return e(r,R),al&&Gt(r,w),Y;if(R===null){for(;!il.done;w++,il=h.next())il=E(r,il.value,x),il!==null&&(s=n(il,s,w),cl===null?Y=il:cl.sibling=il,cl=il);return al&&Gt(r,w),Y}for(R=a(R);!il.done;w++,il=h.next())il=b(R,r,w,il.value,x),il!==null&&(l&&il.alternate!==null&&R.delete(il.key===null?w:il.key),s=n(il,s,w),cl===null?Y=il:cl.sibling=il,cl=il);return l&&R.forEach(function(rm){return t(r,rm)}),al&&Gt(r,w),Y}function hl(r,s,h,x){if(typeof h=="object"&&h!==null&&h.type===xl&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case El:l:{for(var Y=h.key;s!==null;){if(s.key===Y){if(Y=h.type,Y===xl){if(s.tag===7){e(r,s.sibling),x=u(s,h.props.children),x.return=r,r=x;break l}}else if(s.elementType===Y||typeof Y=="object"&&Y!==null&&Y.$$typeof===fl&&Ge(Y)===s.type){e(r,s.sibling),x=u(s,h.props),Ia(x,h),x.return=r,r=x;break l}e(r,s);break}else t(r,s);s=s.sibling}h.type===xl?(x=He(h.props.children,r.mode,x,h.key),x.return=r,r=x):(x=Ju(h.type,h.key,h.props,null,r.mode,x),Ia(x,h),x.return=r,r=x)}return c(r);case _l:l:{for(Y=h.key;s!==null;){if(s.key===Y)if(s.tag===4&&s.stateNode.containerInfo===h.containerInfo&&s.stateNode.implementation===h.implementation){e(r,s.sibling),x=u(s,h.children||[]),x.return=r,r=x;break l}else{e(r,s);break}else t(r,s);s=s.sibling}x=Oc(h,r.mode,x),x.return=r,r=x}return c(r);case fl:return h=Ge(h),hl(r,s,h,x)}if(Wl(h))return j(r,s,h,x);if(Ol(h)){if(Y=Ol(h),typeof Y!="function")throw Error(y(150));return h=Y.call(h),G(r,s,h,x)}if(typeof h.then=="function")return hl(r,s,ln(h),x);if(h.$$typeof===K)return hl(r,s,Wu(r,h),x);tn(r,h)}return typeof h=="string"&&h!==""||typeof h=="number"||typeof h=="bigint"?(h=""+h,s!==null&&s.tag===6?(e(r,s.sibling),x=u(s,h),x.return=r,r=x):(e(r,s),x=Nc(h,r.mode,x),x.return=r,r=x),c(r)):e(r,s)}return function(r,s,h,x){try{Fa=0;var Y=hl(r,s,h,x);return ha=null,Y}catch(R){if(R===ma||R===Iu)throw R;var cl=ot(29,R,null,r.mode);return cl.lanes=x,cl.return=r,cl}finally{}}}var Qe=Hs(!0),qs=Hs(!1),oe=!1;function Gc(l){l.updateQueue={baseState:l.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Xc(l,t){l=l.updateQueue,t.updateQueue===l&&(t.updateQueue={baseState:l.baseState,firstBaseUpdate:l.firstBaseUpdate,lastBaseUpdate:l.lastBaseUpdate,shared:l.shared,callbacks:null})}function re(l){return{lane:l,tag:0,payload:null,callback:null,next:null}}function de(l,t,e){var a=l.updateQueue;if(a===null)return null;if(a=a.shared,(sl&2)!==0){var u=a.pending;return u===null?t.next=t:(t.next=u.next,u.next=t),a.pending=t,t=Ku(l),Ss(l,null,e),t}return wu(l,a,t,e),Ku(l)}function Pa(l,t,e){if(t=t.updateQueue,t!==null&&(t=t.shared,(e&4194048)!==0)){var a=t.lanes;a&=l.pendingLanes,e|=a,t.lanes=e,Nf(l,e)}}function Qc(l,t){var e=l.updateQueue,a=l.alternate;if(a!==null&&(a=a.updateQueue,e===a)){var u=null,n=null;if(e=e.firstBaseUpdate,e!==null){do{var c={lane:e.lane,tag:e.tag,payload:e.payload,callback:null,next:null};n===null?u=n=c:n=n.next=c,e=e.next}while(e!==null);n===null?u=n=t:n=n.next=t}else u=n=t;e={baseState:a.baseState,firstBaseUpdate:u,lastBaseUpdate:n,shared:a.shared,callbacks:a.callbacks},l.updateQueue=e;return}l=e.lastBaseUpdate,l===null?e.firstBaseUpdate=t:l.next=t,e.lastBaseUpdate=t}var Zc=!1;function lu(){if(Zc){var l=ya;if(l!==null)throw l}}function tu(l,t,e,a){Zc=!1;var u=l.updateQueue;oe=!1;var n=u.firstBaseUpdate,c=u.lastBaseUpdate,i=u.shared.pending;if(i!==null){u.shared.pending=null;var f=i,v=f.next;f.next=null,c===null?n=v:c.next=v,c=f;var p=l.alternate;p!==null&&(p=p.updateQueue,i=p.lastBaseUpdate,i!==c&&(i===null?p.firstBaseUpdate=v:i.next=v,p.lastBaseUpdate=f))}if(n!==null){var E=u.baseState;c=0,p=v=f=null,i=n;do{var g=i.lane&-536870913,b=g!==i.lane;if(b?(I&g)===g:(a&g)===g){g!==0&&g===da&&(Zc=!0),p!==null&&(p=p.next={lane:0,tag:i.tag,payload:i.payload,callback:null,next:null});l:{var j=l,G=i;g=t;var hl=e;switch(G.tag){case 1:if(j=G.payload,typeof j=="function"){E=j.call(hl,E,g);break l}E=j;break l;case 3:j.flags=j.flags&-65537|128;case 0:if(j=G.payload,g=typeof j=="function"?j.call(hl,E,g):j,g==null)break l;E=C({},E,g);break l;case 2:oe=!0}}g=i.callback,g!==null&&(l.flags|=64,b&&(l.flags|=8192),b=u.callbacks,b===null?u.callbacks=[g]:b.push(g))}else b={lane:g,tag:i.tag,payload:i.payload,callback:i.callback,next:null},p===null?(v=p=b,f=E):p=p.next=b,c|=g;if(i=i.next,i===null){if(i=u.shared.pending,i===null)break;b=i,i=b.next,b.next=null,u.lastBaseUpdate=b,u.shared.pending=null}}while(!0);p===null&&(f=E),u.baseState=f,u.firstBaseUpdate=v,u.lastBaseUpdate=p,n===null&&(u.shared.lanes=0),ge|=c,l.lanes=c,l.memoizedState=E}}function Ys(l,t){if(typeof l!="function")throw Error(y(191,l));l.call(t)}function Bs(l,t){var e=l.callbacks;if(e!==null)for(l.callbacks=null,l=0;ln?n:8;var c=m.T,i={};m.T=i,fi(l,!1,t,e);try{var f=u(),v=m.S;if(v!==null&&v(i,f),f!==null&&typeof f=="object"&&typeof f.then=="function"){var p=ly(f,a);uu(l,t,p,ht(l))}else uu(l,t,a,ht(l))}catch(E){uu(l,t,{then:function(){},status:"rejected",reason:E},ht())}finally{A.p=n,c!==null&&i.types!==null&&(c.types=i.types),m.T=c}}function cy(){}function ci(l,t,e,a){if(l.tag!==5)throw Error(y(476));var u=go(l).queue;vo(l,u,t,D,e===null?cy:function(){return bo(l),e(a)})}function go(l){var t=l.memoizedState;if(t!==null)return t;t={memoizedState:D,baseState:D,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Vt,lastRenderedState:D},next:null};var e={};return t.next={memoizedState:e,baseState:e,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Vt,lastRenderedState:e},next:null},l.memoizedState=t,l=l.alternate,l!==null&&(l.memoizedState=t),t}function bo(l){var t=go(l);t.next===null&&(t=l.alternate.memoizedState),uu(l,t.next.queue,{},ht())}function ii(){return wl(xu)}function So(){return Rl().memoizedState}function po(){return Rl().memoizedState}function iy(l){for(var t=l.return;t!==null;){switch(t.tag){case 24:case 3:var e=ht();l=re(e);var a=de(t,l,e);a!==null&&(nt(a,t,e),Pa(a,t,e)),t={cache:qc()},l.payload=t;return}t=t.return}}function fy(l,t,e){var a=ht();e={lane:a,revertLane:0,gesture:null,action:e,hasEagerState:!1,eagerState:null,next:null},dn(l)?zo(t,e):(e=Ac(l,t,e,a),e!==null&&(nt(e,l,a),Eo(e,t,a)))}function xo(l,t,e){var a=ht();uu(l,t,e,a)}function uu(l,t,e,a){var u={lane:a,revertLane:0,gesture:null,action:e,hasEagerState:!1,eagerState:null,next:null};if(dn(l))zo(t,u);else{var n=l.alternate;if(l.lanes===0&&(n===null||n.lanes===0)&&(n=t.lastRenderedReducer,n!==null))try{var c=t.lastRenderedState,i=n(c,e);if(u.hasEagerState=!0,u.eagerState=i,st(i,c))return wu(l,t,u,0),gl===null&&Vu(),!1}catch{}finally{}if(e=Ac(l,t,u,a),e!==null)return nt(e,l,a),Eo(e,t,a),!0}return!1}function fi(l,t,e,a){if(a={lane:2,revertLane:Gi(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},dn(l)){if(t)throw Error(y(479))}else t=Ac(l,e,a,2),t!==null&&nt(t,l,2)}function dn(l){var t=l.alternate;return l===V||t!==null&&t===V}function zo(l,t){ga=un=!0;var e=l.pending;e===null?t.next=t:(t.next=e.next,e.next=t),l.pending=t}function Eo(l,t,e){if((e&4194048)!==0){var a=t.lanes;a&=l.pendingLanes,e|=a,t.lanes=e,Nf(l,e)}}var nu={readContext:wl,use:fn,useCallback:Ml,useContext:Ml,useEffect:Ml,useImperativeHandle:Ml,useLayoutEffect:Ml,useInsertionEffect:Ml,useMemo:Ml,useReducer:Ml,useRef:Ml,useState:Ml,useDebugValue:Ml,useDeferredValue:Ml,useTransition:Ml,useSyncExternalStore:Ml,useId:Ml,useHostTransitionStatus:Ml,useFormState:Ml,useActionState:Ml,useOptimistic:Ml,useMemoCache:Ml,useCacheRefresh:Ml};nu.useEffectEvent=Ml;var To={readContext:wl,use:fn,useCallback:function(l,t){return Il().memoizedState=[l,t===void 0?null:t],l},useContext:wl,useEffect:co,useImperativeHandle:function(l,t,e){e=e!=null?e.concat([l]):null,on(4194308,4,oo.bind(null,t,l),e)},useLayoutEffect:function(l,t){return on(4194308,4,l,t)},useInsertionEffect:function(l,t){on(4,2,l,t)},useMemo:function(l,t){var e=Il();t=t===void 0?null:t;var a=l();if(Ze){ae(!0);try{l()}finally{ae(!1)}}return e.memoizedState=[a,t],a},useReducer:function(l,t,e){var a=Il();if(e!==void 0){var u=e(t);if(Ze){ae(!0);try{e(t)}finally{ae(!1)}}}else u=t;return a.memoizedState=a.baseState=u,l={pending:null,lanes:0,dispatch:null,lastRenderedReducer:l,lastRenderedState:u},a.queue=l,l=l.dispatch=fy.bind(null,V,l),[a.memoizedState,l]},useRef:function(l){var t=Il();return l={current:l},t.memoizedState=l},useState:function(l){l=ti(l);var t=l.queue,e=xo.bind(null,V,t);return t.dispatch=e,[l.memoizedState,e]},useDebugValue:ui,useDeferredValue:function(l,t){var e=Il();return ni(e,l,t)},useTransition:function(){var l=ti(!1);return l=vo.bind(null,V,l.queue,!0,!1),Il().memoizedState=l,[!1,l]},useSyncExternalStore:function(l,t,e){var a=V,u=Il();if(al){if(e===void 0)throw Error(y(407));e=e()}else{if(e=t(),gl===null)throw Error(y(349));(I&127)!==0||Vs(a,t,e)}u.memoizedState=e;var n={value:e,getSnapshot:t};return u.queue=n,co(Ks.bind(null,a,n,l),[l]),a.flags|=2048,Sa(9,{destroy:void 0},ws.bind(null,a,n,e,t),null),e},useId:function(){var l=Il(),t=gl.identifierPrefix;if(al){var e=Dt,a=jt;e=(a&~(1<<32-ft(a)-1)).toString(32)+e,t="_"+t+"R_"+e,e=nn++,0<\/script>",n=n.removeChild(n.firstChild);break;case"select":n=typeof a.is=="string"?c.createElement("select",{is:a.is}):c.createElement("select"),a.multiple?n.multiple=!0:a.size&&(n.size=a.size);break;default:n=typeof a.is=="string"?c.createElement(u,{is:a.is}):c.createElement(u)}}n[Zl]=t,n[Pl]=a;l:for(c=t.child;c!==null;){if(c.tag===5||c.tag===6)n.appendChild(c.stateNode);else if(c.tag!==4&&c.tag!==27&&c.child!==null){c.child.return=c,c=c.child;continue}if(c===t)break l;for(;c.sibling===null;){if(c.return===null||c.return===t)break l;c=c.return}c.sibling.return=c.return,c=c.sibling}t.stateNode=n;l:switch(Jl(n,u,a),u){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break l;case"img":a=!0;break l;default:a=!1}a&&Kt(t)}}return pl(t),zi(t,t.type,l===null?null:l.memoizedProps,t.pendingProps,e),null;case 6:if(l&&t.stateNode!=null)l.memoizedProps!==a&&Kt(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(y(166));if(l=k.current,oa(t)){if(l=t.stateNode,e=t.memoizedProps,a=null,u=Vl,u!==null)switch(u.tag){case 27:case 5:a=u.memoizedProps}l[Zl]=t,l=!!(l.nodeValue===e||a!==null&&a.suppressHydrationWarning===!0||Zr(l.nodeValue,e)),l||fe(t,!0)}else l=Cn(l).createTextNode(a),l[Zl]=t,t.stateNode=l}return pl(t),null;case 31:if(e=t.memoizedState,l===null||l.memoizedState!==null){if(a=oa(t),e!==null){if(l===null){if(!a)throw Error(y(318));if(l=t.memoizedState,l=l!==null?l.dehydrated:null,!l)throw Error(y(557));l[Zl]=t}else qe(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;pl(t),l=!1}else e=Cc(),l!==null&&l.memoizedState!==null&&(l.memoizedState.hydrationErrors=e),l=!0;if(!l)return t.flags&256?(dt(t),t):(dt(t),null);if((t.flags&128)!==0)throw Error(y(558))}return pl(t),null;case 13:if(a=t.memoizedState,l===null||l.memoizedState!==null&&l.memoizedState.dehydrated!==null){if(u=oa(t),a!==null&&a.dehydrated!==null){if(l===null){if(!u)throw Error(y(318));if(u=t.memoizedState,u=u!==null?u.dehydrated:null,!u)throw Error(y(317));u[Zl]=t}else qe(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;pl(t),u=!1}else u=Cc(),l!==null&&l.memoizedState!==null&&(l.memoizedState.hydrationErrors=u),u=!0;if(!u)return t.flags&256?(dt(t),t):(dt(t),null)}return dt(t),(t.flags&128)!==0?(t.lanes=e,t):(e=a!==null,l=l!==null&&l.memoizedState!==null,e&&(a=t.child,u=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(u=a.alternate.memoizedState.cachePool.pool),n=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(n=a.memoizedState.cachePool.pool),n!==u&&(a.flags|=2048)),e!==l&&e&&(t.child.flags|=8192),gn(t,t.updateQueue),pl(t),null);case 4:return Cl(),l===null&&Vi(t.stateNode.containerInfo),pl(t),null;case 10:return Qt(t.type),pl(t),null;case 19:if(T(Ul),a=t.memoizedState,a===null)return pl(t),null;if(u=(t.flags&128)!==0,n=a.rendering,n===null)if(u)iu(a,!1);else{if(jl!==0||l!==null&&(l.flags&128)!==0)for(l=t.child;l!==null;){if(n=an(l),n!==null){for(t.flags|=128,iu(a,!1),l=n.updateQueue,t.updateQueue=l,gn(t,l),t.subtreeFlags=0,l=e,e=t.child;e!==null;)ps(e,l),e=e.sibling;return N(Ul,Ul.current&1|2),al&&Gt(t,a.treeForkCount),t.child}l=l.sibling}a.tail!==null&&ct()>zn&&(t.flags|=128,u=!0,iu(a,!1),t.lanes=4194304)}else{if(!u)if(l=an(n),l!==null){if(t.flags|=128,u=!0,l=l.updateQueue,t.updateQueue=l,gn(t,l),iu(a,!0),a.tail===null&&a.tailMode==="hidden"&&!n.alternate&&!al)return pl(t),null}else 2*ct()-a.renderingStartTime>zn&&e!==536870912&&(t.flags|=128,u=!0,iu(a,!1),t.lanes=4194304);a.isBackwards?(n.sibling=t.child,t.child=n):(l=a.last,l!==null?l.sibling=n:t.child=n,a.last=n)}return a.tail!==null?(l=a.tail,a.rendering=l,a.tail=l.sibling,a.renderingStartTime=ct(),l.sibling=null,e=Ul.current,N(Ul,u?e&1|2:e&1),al&&Gt(t,a.treeForkCount),l):(pl(t),null);case 22:case 23:return dt(t),wc(),a=t.memoizedState!==null,l!==null?l.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(e&536870912)!==0&&(t.flags&128)===0&&(pl(t),t.subtreeFlags&6&&(t.flags|=8192)):pl(t),e=t.updateQueue,e!==null&&gn(t,e.retryQueue),e=null,l!==null&&l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(e=l.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==e&&(t.flags|=2048),l!==null&&T(Le),null;case 24:return e=null,l!==null&&(e=l.memoizedState.cache),t.memoizedState.cache!==e&&(t.flags|=2048),Qt(Hl),pl(t),null;case 25:return null;case 30:return null}throw Error(y(156,t.tag))}function yy(l,t){switch(jc(t),t.tag){case 1:return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 3:return Qt(Hl),Cl(),l=t.flags,(l&65536)!==0&&(l&128)===0?(t.flags=l&-65537|128,t):null;case 26:case 27:case 5:return Nu(t),null;case 31:if(t.memoizedState!==null){if(dt(t),t.alternate===null)throw Error(y(340));qe()}return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 13:if(dt(t),l=t.memoizedState,l!==null&&l.dehydrated!==null){if(t.alternate===null)throw Error(y(340));qe()}return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 19:return T(Ul),null;case 4:return Cl(),null;case 10:return Qt(t.type),null;case 22:case 23:return dt(t),wc(),l!==null&&T(Le),l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 24:return Qt(Hl),null;case 25:return null;default:return null}}function ko(l,t){switch(jc(t),t.tag){case 3:Qt(Hl),Cl();break;case 26:case 27:case 5:Nu(t);break;case 4:Cl();break;case 31:t.memoizedState!==null&&dt(t);break;case 13:dt(t);break;case 19:T(Ul);break;case 10:Qt(t.type);break;case 22:case 23:dt(t),wc(),l!==null&&T(Le);break;case 24:Qt(Hl)}}function fu(l,t){try{var e=t.updateQueue,a=e!==null?e.lastEffect:null;if(a!==null){var u=a.next;e=u;do{if((e.tag&l)===l){a=void 0;var n=e.create,c=e.inst;a=n(),c.destroy=a}e=e.next}while(e!==u)}}catch(i){dl(t,t.return,i)}}function he(l,t,e){try{var a=t.updateQueue,u=a!==null?a.lastEffect:null;if(u!==null){var n=u.next;a=n;do{if((a.tag&l)===l){var c=a.inst,i=c.destroy;if(i!==void 0){c.destroy=void 0,u=t;var f=e,v=i;try{v()}catch(p){dl(u,f,p)}}}a=a.next}while(a!==n)}}catch(p){dl(t,t.return,p)}}function $o(l){var t=l.updateQueue;if(t!==null){var e=l.stateNode;try{Bs(t,e)}catch(a){dl(l,l.return,a)}}}function Wo(l,t,e){e.props=Ve(l.type,l.memoizedProps),e.state=l.memoizedState;try{e.componentWillUnmount()}catch(a){dl(l,t,a)}}function su(l,t){try{var e=l.ref;if(e!==null){switch(l.tag){case 26:case 27:case 5:var a=l.stateNode;break;case 30:a=l.stateNode;break;default:a=l.stateNode}typeof e=="function"?l.refCleanup=e(a):e.current=a}}catch(u){dl(l,t,u)}}function Ct(l,t){var e=l.ref,a=l.refCleanup;if(e!==null)if(typeof a=="function")try{a()}catch(u){dl(l,t,u)}finally{l.refCleanup=null,l=l.alternate,l!=null&&(l.refCleanup=null)}else if(typeof e=="function")try{e(null)}catch(u){dl(l,t,u)}else e.current=null}function Fo(l){var t=l.type,e=l.memoizedProps,a=l.stateNode;try{l:switch(t){case"button":case"input":case"select":case"textarea":e.autoFocus&&a.focus();break l;case"img":e.src?a.src=e.src:e.srcSet&&(a.srcset=e.srcSet)}}catch(u){dl(l,l.return,u)}}function Ei(l,t,e){try{var a=l.stateNode;Hy(a,l.type,e,t),a[Pl]=t}catch(u){dl(l,l.return,u)}}function Io(l){return l.tag===5||l.tag===3||l.tag===26||l.tag===27&&ze(l.type)||l.tag===4}function Ti(l){l:for(;;){for(;l.sibling===null;){if(l.return===null||Io(l.return))return null;l=l.return}for(l.sibling.return=l.return,l=l.sibling;l.tag!==5&&l.tag!==6&&l.tag!==18;){if(l.tag===27&&ze(l.type)||l.flags&2||l.child===null||l.tag===4)continue l;l.child.return=l,l=l.child}if(!(l.flags&2))return l.stateNode}}function Ai(l,t,e){var a=l.tag;if(a===5||a===6)l=l.stateNode,t?(e.nodeType===9?e.body:e.nodeName==="HTML"?e.ownerDocument.body:e).insertBefore(l,t):(t=e.nodeType===9?e.body:e.nodeName==="HTML"?e.ownerDocument.body:e,t.appendChild(l),e=e._reactRootContainer,e!=null||t.onclick!==null||(t.onclick=Yt));else if(a!==4&&(a===27&&ze(l.type)&&(e=l.stateNode,t=null),l=l.child,l!==null))for(Ai(l,t,e),l=l.sibling;l!==null;)Ai(l,t,e),l=l.sibling}function bn(l,t,e){var a=l.tag;if(a===5||a===6)l=l.stateNode,t?e.insertBefore(l,t):e.appendChild(l);else if(a!==4&&(a===27&&ze(l.type)&&(e=l.stateNode),l=l.child,l!==null))for(bn(l,t,e),l=l.sibling;l!==null;)bn(l,t,e),l=l.sibling}function Po(l){var t=l.stateNode,e=l.memoizedProps;try{for(var a=l.type,u=t.attributes;u.length;)t.removeAttributeNode(u[0]);Jl(t,a,e),t[Zl]=l,t[Pl]=e}catch(n){dl(l,l.return,n)}}var Jt=!1,Bl=!1,_i=!1,lr=typeof WeakSet=="function"?WeakSet:Set,Xl=null;function my(l,t){if(l=l.containerInfo,Ji=Ln,l=rs(l),Sc(l)){if("selectionStart"in l)var e={start:l.selectionStart,end:l.selectionEnd};else l:{e=(e=l.ownerDocument)&&e.defaultView||window;var a=e.getSelection&&e.getSelection();if(a&&a.rangeCount!==0){e=a.anchorNode;var u=a.anchorOffset,n=a.focusNode;a=a.focusOffset;try{e.nodeType,n.nodeType}catch{e=null;break l}var c=0,i=-1,f=-1,v=0,p=0,E=l,g=null;t:for(;;){for(var b;E!==e||u!==0&&E.nodeType!==3||(i=c+u),E!==n||a!==0&&E.nodeType!==3||(f=c+a),E.nodeType===3&&(c+=E.nodeValue.length),(b=E.firstChild)!==null;)g=E,E=b;for(;;){if(E===l)break t;if(g===e&&++v===u&&(i=c),g===n&&++p===a&&(f=c),(b=E.nextSibling)!==null)break;E=g,g=E.parentNode}E=b}e=i===-1||f===-1?null:{start:i,end:f}}else e=null}e=e||{start:0,end:0}}else e=null;for(ki={focusedElem:l,selectionRange:e},Ln=!1,Xl=t;Xl!==null;)if(t=Xl,l=t.child,(t.subtreeFlags&1028)!==0&&l!==null)l.return=t,Xl=l;else for(;Xl!==null;){switch(t=Xl,n=t.alternate,l=t.flags,t.tag){case 0:if((l&4)!==0&&(l=t.updateQueue,l=l!==null?l.events:null,l!==null))for(e=0;e title"))),Jl(n,a,e),n[Zl]=l,Gl(n),a=n;break l;case"link":var c=c0("link","href",u).get(a+(e.href||""));if(c){for(var i=0;ihl&&(c=hl,hl=G,G=c);var r=ss(i,G),s=ss(i,hl);if(r&&s&&(b.rangeCount!==1||b.anchorNode!==r.node||b.anchorOffset!==r.offset||b.focusNode!==s.node||b.focusOffset!==s.offset)){var h=E.createRange();h.setStart(r.node,r.offset),b.removeAllRanges(),G>hl?(b.addRange(h),b.extend(s.node,s.offset)):(h.setEnd(s.node,s.offset),b.addRange(h))}}}}for(E=[],b=i;b=b.parentNode;)b.nodeType===1&&E.push({element:b,left:b.scrollLeft,top:b.scrollTop});for(typeof i.focus=="function"&&i.focus(),i=0;ie?32:e,m.T=null,e=Ui,Ui=null;var n=Se,c=It;if(Ll=0,Ta=Se=null,It=0,(sl&6)!==0)throw Error(y(331));var i=sl;if(sl|=4,rr(n.current),fr(n,n.current,c,e),sl=i,hu(0,!1),it&&typeof it.onPostCommitFiberRoot=="function")try{it.onPostCommitFiberRoot(Ua,n)}catch{}return!0}finally{A.p=u,m.T=a,Mr(l,t)}}function Dr(l,t,e){t=St(e,t),t=di(l.stateNode,t,2),l=de(l,t,2),l!==null&&(Ha(l,2),Ut(l))}function dl(l,t,e){if(l.tag===3)Dr(l,l,e);else for(;t!==null;){if(t.tag===3){Dr(t,l,e);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(be===null||!be.has(a))){l=St(e,l),e=Co(2),a=de(t,e,2),a!==null&&(Uo(e,a,t,l),Ha(a,2),Ut(a));break}}t=t.return}}function Yi(l,t,e){var a=l.pingCache;if(a===null){a=l.pingCache=new gy;var u=new Set;a.set(t,u)}else u=a.get(t),u===void 0&&(u=new Set,a.set(t,u));u.has(e)||(Mi=!0,u.add(e),l=zy.bind(null,l,t,e),t.then(l,l))}function zy(l,t,e){var a=l.pingCache;a!==null&&a.delete(t),l.pingedLanes|=l.suspendedLanes&e,l.warmLanes&=~e,gl===l&&(I&e)===e&&(jl===4||jl===3&&(I&62914560)===I&&300>ct()-xn?(sl&2)===0&&Aa(l,0):ji|=e,Ea===I&&(Ea=0)),Ut(l)}function Cr(l,t){t===0&&(t=Af()),l=Re(l,t),l!==null&&(Ha(l,t),Ut(l))}function Ey(l){var t=l.memoizedState,e=0;t!==null&&(e=t.retryLane),Cr(l,e)}function Ty(l,t){var e=0;switch(l.tag){case 31:case 13:var a=l.stateNode,u=l.memoizedState;u!==null&&(e=u.retryLane);break;case 19:a=l.stateNode;break;case 22:a=l.stateNode._retryCache;break;default:throw Error(y(314))}a!==null&&a.delete(t),Cr(l,e)}function Ay(l,t){return Wn(l,t)}var On=null,Na=null,Bi=!1,Mn=!1,Li=!1,xe=0;function Ut(l){l!==Na&&l.next===null&&(Na===null?On=Na=l:Na=Na.next=l),Mn=!0,Bi||(Bi=!0,Ny())}function hu(l,t){if(!Li&&Mn){Li=!0;do for(var e=!1,a=On;a!==null;){if(l!==0){var u=a.pendingLanes;if(u===0)var n=0;else{var c=a.suspendedLanes,i=a.pingedLanes;n=(1<<31-ft(42|l)+1)-1,n&=u&~(c&~i),n=n&201326741?n&201326741|1:n?n|2:0}n!==0&&(e=!0,qr(a,n))}else n=I,n=Cu(a,a===gl?n:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(n&3)===0||Ra(a,n)||(e=!0,qr(a,n));a=a.next}while(e);Li=!1}}function _y(){Ur()}function Ur(){Mn=Bi=!1;var l=0;xe!==0&&Yy()&&(l=xe);for(var t=ct(),e=null,a=On;a!==null;){var u=a.next,n=Rr(a,t);n===0?(a.next=null,e===null?On=u:e.next=u,u===null&&(Na=e)):(e=a,(l!==0||(n&3)!==0)&&(Mn=!0)),a=u}Ll!==0&&Ll!==5||hu(l),xe!==0&&(xe=0)}function Rr(l,t){for(var e=l.suspendedLanes,a=l.pingedLanes,u=l.expirationTimes,n=l.pendingLanes&-62914561;0i)break;var p=f.transferSize,E=f.initiatorType;p&&Vr(E)&&(f=f.responseEnd,c+=p*(f"u"?null:document;function e0(l,t,e){var a=Oa;if(a&&typeof t=="string"&&t){var u=gt(t);u='link[rel="'+l+'"][href="'+u+'"]',typeof e=="string"&&(u+='[crossorigin="'+e+'"]'),t0.has(u)||(t0.add(u),l={rel:l,crossOrigin:e,href:t},a.querySelector(u)===null&&(t=a.createElement("link"),Jl(t,"link",l),Gl(t),a.head.appendChild(t)))}}function Ky(l){Pt.D(l),e0("dns-prefetch",l,null)}function Jy(l,t){Pt.C(l,t),e0("preconnect",l,t)}function ky(l,t,e){Pt.L(l,t,e);var a=Oa;if(a&&l&&t){var u='link[rel="preload"][as="'+gt(t)+'"]';t==="image"&&e&&e.imageSrcSet?(u+='[imagesrcset="'+gt(e.imageSrcSet)+'"]',typeof e.imageSizes=="string"&&(u+='[imagesizes="'+gt(e.imageSizes)+'"]')):u+='[href="'+gt(l)+'"]';var n=u;switch(t){case"style":n=Ma(l);break;case"script":n=ja(l)}At.has(n)||(l=C({rel:"preload",href:t==="image"&&e&&e.imageSrcSet?void 0:l,as:t},e),At.set(n,l),a.querySelector(u)!==null||t==="style"&&a.querySelector(Su(n))||t==="script"&&a.querySelector(pu(n))||(t=a.createElement("link"),Jl(t,"link",l),Gl(t),a.head.appendChild(t)))}}function $y(l,t){Pt.m(l,t);var e=Oa;if(e&&l){var a=t&&typeof t.as=="string"?t.as:"script",u='link[rel="modulepreload"][as="'+gt(a)+'"][href="'+gt(l)+'"]',n=u;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":n=ja(l)}if(!At.has(n)&&(l=C({rel:"modulepreload",href:l},t),At.set(n,l),e.querySelector(u)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(e.querySelector(pu(n)))return}a=e.createElement("link"),Jl(a,"link",l),Gl(a),e.head.appendChild(a)}}}function Wy(l,t,e){Pt.S(l,t,e);var a=Oa;if(a&&l){var u=Fe(a).hoistableStyles,n=Ma(l);t=t||"default";var c=u.get(n);if(!c){var i={loading:0,preload:null};if(c=a.querySelector(Su(n)))i.loading=5;else{l=C({rel:"stylesheet",href:l,"data-precedence":t},e),(e=At.get(n))&&tf(l,e);var f=c=a.createElement("link");Gl(f),Jl(f,"link",l),f._p=new Promise(function(v,p){f.onload=v,f.onerror=p}),f.addEventListener("load",function(){i.loading|=1}),f.addEventListener("error",function(){i.loading|=2}),i.loading|=4,Rn(c,t,a)}c={type:"stylesheet",instance:c,count:1,state:i},u.set(n,c)}}}function Fy(l,t){Pt.X(l,t);var e=Oa;if(e&&l){var a=Fe(e).hoistableScripts,u=ja(l),n=a.get(u);n||(n=e.querySelector(pu(u)),n||(l=C({src:l,async:!0},t),(t=At.get(u))&&ef(l,t),n=e.createElement("script"),Gl(n),Jl(n,"link",l),e.head.appendChild(n)),n={type:"script",instance:n,count:1,state:null},a.set(u,n))}}function Iy(l,t){Pt.M(l,t);var e=Oa;if(e&&l){var a=Fe(e).hoistableScripts,u=ja(l),n=a.get(u);n||(n=e.querySelector(pu(u)),n||(l=C({src:l,async:!0,type:"module"},t),(t=At.get(u))&&ef(l,t),n=e.createElement("script"),Gl(n),Jl(n,"link",l),e.head.appendChild(n)),n={type:"script",instance:n,count:1,state:null},a.set(u,n))}}function a0(l,t,e,a){var u=(u=k.current)?Un(u):null;if(!u)throw Error(y(446));switch(l){case"meta":case"title":return null;case"style":return typeof e.precedence=="string"&&typeof e.href=="string"?(t=Ma(e.href),e=Fe(u).hoistableStyles,a=e.get(t),a||(a={type:"style",instance:null,count:0,state:null},e.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(e.rel==="stylesheet"&&typeof e.href=="string"&&typeof e.precedence=="string"){l=Ma(e.href);var n=Fe(u).hoistableStyles,c=n.get(l);if(c||(u=u.ownerDocument||u,c={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},n.set(l,c),(n=u.querySelector(Su(l)))&&!n._p&&(c.instance=n,c.state.loading=5),At.has(l)||(e={rel:"preload",as:"style",href:e.href,crossOrigin:e.crossOrigin,integrity:e.integrity,media:e.media,hrefLang:e.hrefLang,referrerPolicy:e.referrerPolicy},At.set(l,e),n||Py(u,l,e,c.state))),t&&a===null)throw Error(y(528,""));return c}if(t&&a!==null)throw Error(y(529,""));return null;case"script":return t=e.async,e=e.src,typeof e=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=ja(e),e=Fe(u).hoistableScripts,a=e.get(t),a||(a={type:"script",instance:null,count:0,state:null},e.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(y(444,l))}}function Ma(l){return'href="'+gt(l)+'"'}function Su(l){return'link[rel="stylesheet"]['+l+"]"}function u0(l){return C({},l,{"data-precedence":l.precedence,precedence:null})}function Py(l,t,e,a){l.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=l.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),Jl(t,"link",e),Gl(t),l.head.appendChild(t))}function ja(l){return'[src="'+gt(l)+'"]'}function pu(l){return"script[async]"+l}function n0(l,t,e){if(t.count++,t.instance===null)switch(t.type){case"style":var a=l.querySelector('style[data-href~="'+gt(e.href)+'"]');if(a)return t.instance=a,Gl(a),a;var u=C({},e,{"data-href":e.href,"data-precedence":e.precedence,href:null,precedence:null});return a=(l.ownerDocument||l).createElement("style"),Gl(a),Jl(a,"style",u),Rn(a,e.precedence,l),t.instance=a;case"stylesheet":u=Ma(e.href);var n=l.querySelector(Su(u));if(n)return t.state.loading|=4,t.instance=n,Gl(n),n;a=u0(e),(u=At.get(u))&&tf(a,u),n=(l.ownerDocument||l).createElement("link"),Gl(n);var c=n;return c._p=new Promise(function(i,f){c.onload=i,c.onerror=f}),Jl(n,"link",a),t.state.loading|=4,Rn(n,e.precedence,l),t.instance=n;case"script":return n=ja(e.src),(u=l.querySelector(pu(n)))?(t.instance=u,Gl(u),u):(a=e,(u=At.get(n))&&(a=C({},e),ef(a,u)),l=l.ownerDocument||l,u=l.createElement("script"),Gl(u),Jl(u,"link",a),l.head.appendChild(u),t.instance=u);case"void":return null;default:throw Error(y(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,Rn(a,e.precedence,l));return t.instance}function Rn(l,t,e){for(var a=e.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),u=a.length?a[a.length-1]:null,n=u,c=0;c title"):null)}function lm(l,t,e){if(e===1||t.itemProp!=null)return!1;switch(l){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return l=t.disabled,typeof t.precedence=="string"&&l==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function f0(l){return!(l.type==="stylesheet"&&(l.state.loading&3)===0)}function tm(l,t,e,a){if(e.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(e.state.loading&4)===0){if(e.instance===null){var u=Ma(a.href),n=t.querySelector(Su(u));if(n){t=n._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(l.count++,l=qn.bind(l),t.then(l,l)),e.state.loading|=4,e.instance=n,Gl(n);return}n=t.ownerDocument||t,a=u0(a),(u=At.get(u))&&tf(a,u),n=n.createElement("link"),Gl(n);var c=n;c._p=new Promise(function(i,f){c.onload=i,c.onerror=f}),Jl(n,"link",a),e.instance=n}l.stylesheets===null&&(l.stylesheets=new Map),l.stylesheets.set(e,t),(t=e.state.preload)&&(e.state.loading&3)===0&&(l.count++,e=qn.bind(l),t.addEventListener("load",e),t.addEventListener("error",e))}}var af=0;function em(l,t){return l.stylesheets&&l.count===0&&Bn(l,l.stylesheets),0af?50:800)+t);return l.unsuspend=e,function(){l.unsuspend=null,clearTimeout(a),clearTimeout(u)}}:null}function qn(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Bn(this,this.stylesheets);else if(this.unsuspend){var l=this.unsuspend;this.unsuspend=null,l()}}}var Yn=null;function Bn(l,t){l.stylesheets=null,l.unsuspend!==null&&(l.count++,Yn=new Map,t.forEach(am,l),Yn=null,qn.call(l))}function am(l,t){if(!(t.state.loading&4)){var e=Yn.get(l);if(e)var a=e.get(null);else{e=new Map,Yn.set(l,e);for(var u=l.querySelectorAll("link[data-precedence],style[data-precedence]"),n=0;n"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(S)}catch(_){console.error(_)}}return S(),yf.exports=pm(),yf.exports}var zm=xm();const Em=Y0(zm);var Je=(S=>(S.LOCAL="LOCAL",S.CLOUD="CLOUD",S))(Je||{}),ee=(S=>(S.LOCAL_OVERWRITE="LOCAL_OVERWRITE",S.CLOUD_OVERWRITE="CLOUD_OVERWRITE",S.MERGE_LOCAL="MERGE_LOCAL",S.MERGE_CLOUD="MERGE_CLOUD",S))(ee||{});const le="/api",te=async S=>{try{return await S.json()}catch(_){return console.error("Failed to parse API response",_),{data:{},status:"error",message:"Invalid response from server"}}},Tm=S=>S===Je.LOCAL?"local":"cloud",Rt={getUiConfig:async()=>{try{const S=await fetch(`${le}/ui-config`),_=await te(S);return!S.ok||_.status!=="success"?{data:{statusCheckIntervalSeconds:60},status:"error",message:_.message||"无法获取前端配置"}:_}catch{return{data:{statusCheckIntervalSeconds:60},status:"error",message:"无法获取前端配置"}}},getSettings:async()=>{try{const S=await fetch(`${le}/settings`),_=await te(S);return!S.ok||_.status!=="success"?{data:{syncStrategy:"LOCAL_OVERWRITE",regexRules:[]},status:"error",message:_.message||"无法获取设置"}:_}catch{return{data:{syncStrategy:"LOCAL_OVERWRITE",regexRules:[]},status:"error",message:"无法获取设置"}}},saveStrategy:async S=>{try{const _=await fetch(`${le}/settings/strategy`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({strategy:S})});return await te(_)}catch{return{data:{syncStrategy:S,regexRules:[]},status:"error",message:"无法保存同步策略"}}},getRegexRules:async()=>{try{const S=await fetch(`${le}/regex-rules`);return await te(S)}catch{return{data:[],status:"error",message:"无法获取正则规则"}}},saveRegexRules:async S=>{try{const _=await fetch(`${le}/regex-rules`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(S)});return await te(_)}catch{return{data:[],status:"error",message:"无法保存正则规则"}}},getPlaylists:async S=>{try{const _=await fetch(`${le}/playlists/${Tm(S)}`),H=await te(_);return!_.ok||H.status!=="success"?{data:[],status:"error",message:H.message||"Failed to fetch playlists"}:H}catch(_){return console.error(`Error fetching ${S} playlists:`,_),{data:[],status:"error",message:"Failed to fetch playlists"}}},getServerStatus:async()=>{try{const S=await fetch(`${le}/server/status`),_=await te(S);return!S.ok||_.status!=="success"?{data:{isConnected:!1},status:"error",message:_.message||"Failed to connect to server"}:_}catch{return{data:{isConnected:!1},status:"error",message:"Failed to connect to server"}}},connectToPlex:async S=>{try{const _=await fetch(`${le}/server/connect`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(S)});return await te(_)}catch(_){return{data:{token:"",serverInfo:{isConnected:!1}},status:"error",message:(_==null?void 0:_.message)||"Connection failed"}}},selectLibrary:async S=>{try{const _=await fetch(`${le}/server/library`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({library:S})});return await te(_)}catch{return{data:{libraryName:S},status:"error",message:"无法切换媒体库"}}}};/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Am=S=>S.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),_m=S=>S.replace(/^([A-Z])|[\s-_]+(\w)/g,(_,H,y)=>y?y.toUpperCase():H.toLowerCase()),C0=S=>{const _=_m(S);return _.charAt(0).toUpperCase()+_.slice(1)},B0=(...S)=>S.filter((_,H,y)=>!!_&&_.trim()!==""&&y.indexOf(_)===H).join(" ").trim(),Nm=S=>{for(const _ in S)if(_.startsWith("aria-")||_==="role"||_==="title")return!0};/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */var Om={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Mm=J.forwardRef(({color:S="currentColor",size:_=24,strokeWidth:H=2,absoluteStrokeWidth:y,className:M="",children:B,iconNode:F,...ll},O)=>J.createElement("svg",{ref:O,...Om,width:_,height:_,stroke:S,strokeWidth:y?Number(H)*24/Number(_):H,className:B0("lucide",M),...!B&&!Nm(ll)&&{"aria-hidden":"true"},...ll},[...F.map(([z,X])=>J.createElement(z,X)),...Array.isArray(B)?B:[B]]));/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const vl=(S,_)=>{const H=J.forwardRef(({className:y,...M},B)=>J.createElement(Mm,{ref:B,iconNode:_,className:B0(`lucide-${Am(C0(S))}`,`lucide-${S}`,y),...M}));return H.displayName=C0(S),H};/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const jm=[["path",{d:"M8 3 4 7l4 4",key:"9rb6wj"}],["path",{d:"M4 7h16",key:"6tx8e3"}],["path",{d:"m16 21 4-4-4-4",key:"siv7j2"}],["path",{d:"M20 17H4",key:"h6l3hr"}]],Dm=vl("arrow-left-right",jm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Cm=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],Um=vl("check",Cm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Rm=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],L0=vl("chevron-down",Rm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Hm=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m12 8-4 4 4 4",key:"15vm53"}],["path",{d:"M16 12H8",key:"1fr5h0"}]],qm=vl("circle-arrow-left",Hm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ym=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m12 16 4-4-4-4",key:"1i9zcv"}],["path",{d:"M8 12h8",key:"1wcyev"}]],G0=vl("circle-arrow-right",Ym);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Bm=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]],Lm=vl("circle-check-big",Bm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Gm=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],Xm=vl("circle-question-mark",Gm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Qm=[["path",{d:"M12 6v6l4 2",key:"mmk7yg"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],Zm=vl("clock",Qm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Vm=[["path",{d:"M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z",key:"p7xjir"}]],wm=vl("cloud",Vm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Km=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M6 12c0-1.7.7-3.2 1.8-4.2",key:"oqkarx"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}],["path",{d:"M18 12c0 1.7-.7 3.2-1.8 4.2",key:"1eah9h"}]],Jm=vl("disc-3",Km);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const km=[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",key:"ct8e1f"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242",key:"151rxh"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143",key:"13bj9a"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],$m=vl("eye-off",km);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Wm=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],Fm=vl("eye",Wm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Im=[["circle",{cx:"18",cy:"18",r:"3",key:"1xkwt0"}],["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M6 21V9a9 9 0 0 0 9 9",key:"7kw0sc"}]],U0=vl("git-merge",Im);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Pm=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]],l1=vl("globe",Pm);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const t1=[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]],e1=vl("key",t1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const a1=[["path",{d:"m16 6 4 14",key:"ji33uf"}],["path",{d:"M12 6v14",key:"1n7gus"}],["path",{d:"M8 8v12",key:"1gg7y9"}],["path",{d:"M4 4v16",key:"6qkkli"}]],u1=vl("library",a1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const n1=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],c1=vl("lock",n1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const i1=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],R0=vl("plus",i1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const f1=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],H0=vl("refresh-cw",f1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const s1=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],o1=vl("rotate-ccw",s1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const r1=[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]],d1=vl("save",r1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const y1=[["path",{d:"M7 2h13a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-5",key:"bt2siv"}],["path",{d:"M10 10 2.5 2.5C2 2 2 2.5 2 5v3a2 2 0 0 0 2 2h6z",key:"1hjrv1"}],["path",{d:"M22 17v-1a2 2 0 0 0-2-2h-1",key:"1iynyr"}],["path",{d:"M4 14a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16.5l1-.5.5.5-8-8H4z",key:"161ggg"}],["path",{d:"M6 18h.01",key:"uhywen"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],m1=vl("server-off",y1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const h1=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],Sf=vl("server",h1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const v1=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],g1=vl("shield-check",v1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const b1=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],S1=vl("trash-2",b1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const p1=[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]],x1=vl("user",p1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const z1=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],E1=vl("wifi-off",z1);/** + * @license lucide-react v0.555.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const T1=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],X0=vl("x",T1),A1=({playlist:S})=>d.jsxs("div",{className:"group flex flex-col w-full p-2.5 bg-gray-800/60 rounded-md border border-gray-700/50 hover:bg-gray-700 hover:border-plex-orange/50 transition-all duration-200 cursor-pointer shadow-sm",children:[d.jsx("div",{className:"flex items-center justify-between",children:d.jsx("h4",{className:"text-sm font-medium text-gray-200 truncate flex-1 mr-2 group-hover:text-white transition-colors",children:S.title})}),d.jsxs("div",{className:"flex items-center mt-1.5 space-x-4 text-xs text-gray-500 group-hover:text-gray-400",children:[d.jsxs("span",{className:"flex items-center",title:"Track Count",children:[d.jsx(Jm,{size:12,className:"mr-1.5 opacity-70"}),S.trackCount]}),d.jsxs("span",{className:"flex items-center",title:"Last Updated",children:[d.jsx(Zm,{size:12,className:"mr-1.5 opacity-70"}),new Date(S.lastUpdated).toLocaleDateString()]})]})]}),q0=({type:S,playlists:_,isLoading:H,onRefresh:y,serverInfo:M})=>{const B=S===Je.LOCAL;let F=B?Sf:wm,ll=B?"text-blue-400":"text-green-400";const O=B?"border-blue-500/30":"border-green-500/30",z=B?"bg-gradient-to-br from-gray-800/80 to-gray-900/80":"bg-gradient-to-bl from-gray-800/80 to-gray-900/80";let X="",C=null;return B?(X="Local Server",C=d.jsxs("p",{className:"text-xs text-gray-400 font-medium mt-0.5 md:mt-0 md:ml-0",children:[_.length," Playlists"]})):M?M.isConnected?(X=M.name||"Cloud Server",C=d.jsxs("div",{className:"flex items-center text-xs text-gray-300 font-medium space-x-1.5 truncate mt-0.5 md:mt-0",children:[d.jsx("span",{className:"text-plex-orange truncate font-semibold",children:M.libraryName}),d.jsx("span",{className:"text-gray-600 hidden md:inline",children:"•"}),d.jsxs("span",{className:"text-gray-500 font-mono text-[10px] hidden md:inline",children:[M.ip,":",M.port]})]})):(X="Not Connected",F=E1,ll="text-red-400",C=d.jsx("p",{className:"text-xs text-gray-500 font-medium mt-0.5",children:"Connection failed"})):(X="Cloud Server",C=d.jsx("p",{className:"text-xs text-gray-500 font-medium mt-0.5",children:H?"Connecting...":"Waiting..."})),d.jsxs("div",{className:`flex flex-row md:flex-col h-full ${z} rounded-2xl border ${O} backdrop-blur-xl shadow-xl overflow-hidden transition-all duration-300`,children:[d.jsxs("div",{className:` + relative flex-none + order-last md:order-first + w-[72px] md:w-full + h-full md:h-auto md:min-h-[80px] + flex flex-col md:flex-row items-center justify-between + py-6 md:py-0 md:px-8 + bg-gray-800/60 border-l md:border-l-0 md:border-b border-white/5 + `,children:[d.jsxs("div",{className:"flex flex-col md:flex-row items-center md:space-x-4 overflow-hidden w-full md:w-auto h-full md:h-full md:py-4",children:[d.jsx("div",{className:`p-2.5 rounded-xl bg-gray-900/50 border border-white/5 ${ll} shadow-inner flex-shrink-0 mb-4 md:mb-0`,children:d.jsx(F,{size:22,strokeWidth:2})}),d.jsx("div",{className:"flex-1 min-w-0 flex flex-col justify-center items-center md:items-start h-full md:h-auto w-full md:w-auto",children:d.jsxs("div",{className:"flex flex-col justify-center w-full md:w-auto [writing-mode:vertical-rl] rotate-180 md:[writing-mode:horizontal-tb] md:rotate-0 items-center md:items-start gap-1 md:gap-0",children:[d.jsx("h2",{className:"text-sm md:text-lg font-bold text-gray-100 tracking-wide whitespace-nowrap",title:X,children:X}),d.jsx("div",{className:"transform md:translate-y-0",children:C})]})})]}),d.jsx("button",{onClick:y,disabled:H,className:"flex-shrink-0 p-2.5 text-gray-400 hover:text-white hover:bg-white/10 rounded-full transition-all active:scale-90 disabled:opacity-50 disabled:cursor-not-allowed mt-4 md:mt-0 md:ml-4",title:"Refresh Playlists",children:d.jsx(H0,{size:20,className:H?"animate-spin text-plex-orange":"",strokeWidth:2})})]}),d.jsx("div",{className:"flex-1 overflow-y-auto p-3 md:p-5 custom-scrollbar bg-black/20",children:H&&_.length===0?d.jsxs("div",{className:"flex flex-col items-center justify-center h-full text-gray-500 space-y-3",children:[d.jsx(H0,{size:24,className:"animate-spin text-plex-orange/50"}),d.jsx("p",{className:"text-xs font-medium tracking-wide uppercase",children:"Syncing..."})]}):_.length===0?d.jsx("div",{className:"flex flex-col items-center justify-center h-full text-gray-500",children:d.jsx("p",{className:"text-sm",children:"No playlists found."})}):d.jsx("div",{className:"space-y-2.5 md:space-y-3",children:_.map(tl=>d.jsx(A1,{playlist:tl},tl.id))})})]})},gf=[{value:ee.LOCAL_OVERWRITE,label:"Local Overwrite",description:"Local playlist completely overwrites Cloud. (No Diff)",icon:G0,color:"text-blue-400"},{value:ee.CLOUD_OVERWRITE,label:"Cloud Overwrite",description:"Cloud playlist completely overwrites Local. (No Diff)",icon:qm,color:"text-green-400"},{value:ee.MERGE_LOCAL,label:"Two-way Merge (Local Priority)",description:"Merge both. Conflicts resolve to Local version.",icon:U0,color:"text-blue-300"},{value:ee.MERGE_CLOUD,label:"Two-way Merge (Cloud Priority)",description:"Merge both. Conflicts resolve to Cloud version.",icon:U0,color:"text-green-300"}],_1=({currentStrategy:S,onSelect:_,savedRegexReplacements:H,onSaveRegex:y})=>{const[M,B]=J.useState(!1),F=J.useRef(null),[ll,O]=J.useState([]),[z,X]=J.useState(!1);J.useEffect(()=>{O(JSON.parse(JSON.stringify(H))),X(!1)},[H]),J.useEffect(()=>{const q=JSON.stringify(ll)!==JSON.stringify(H);X(q)},[ll,H]);const C=gf.find(q=>q.value===S)||gf[0];J.useEffect(()=>{const q=K=>{F.current&&!F.current.contains(K.target)&&B(!1)};return document.addEventListener("mousedown",q),()=>document.removeEventListener("mousedown",q)},[]);const tl=q=>{_(q.value,q.label)},El=()=>{const q=Date.now().toString();O(K=>[...K,{id:q,pattern:"",replacement:""}])},_l=q=>{O(K=>K.filter(nl=>nl.id!==q))},xl=(q,K,nl)=>{O(Ql=>Ql.map(bl=>bl.id===q?{...bl,[K]:nl}:bl))},$l=()=>{O(JSON.parse(JSON.stringify(H)))},Nl=()=>{const q=ll.filter(K=>K.pattern.trim()!=="");O(q),y(q)};return d.jsxs("div",{className:"relative group",ref:F,children:[d.jsxs("button",{onClick:()=>B(!M),className:"flex items-center justify-center w-12 h-12 rounded-full bg-gray-800/90 border border-gray-600 hover:border-plex-orange text-gray-300 hover:text-white hover:bg-gray-700/80 transition-all shadow-2xl hover:shadow-plex-orange/30 ring-1 ring-black/40 backdrop-blur-sm active:scale-95",title:`Current Strategy: ${C.label}`,children:[d.jsx(C.icon,{size:22,className:C.color,strokeWidth:2.5}),d.jsx("div",{className:"absolute -bottom-1 -right-1 bg-gray-900 rounded-full border border-gray-600 p-[2px] shadow-sm",children:d.jsx(L0,{size:10,className:"text-gray-400"})})]}),d.jsxs("div",{className:`absolute + top-14 + /* Mobile: Open to left */ + right-0 origin-top-right + /* Desktop: Center alignment */ + md:left-1/2 md:right-auto md:origin-top md:-translate-x-1/2 + + w-80 md:w-[30rem] bg-gray-800/95 border border-white/10 rounded-xl shadow-2xl z-50 overflow-hidden backdrop-blur-xl + transition-all duration-200 ease-out + ${M?"opacity-100 scale-100 visible translate-y-0":"opacity-0 scale-95 invisible pointer-events-none -translate-y-2"}`,children:[d.jsxs("div",{className:"px-4 py-3 bg-black/20 border-b border-white/5",children:[d.jsx("h3",{className:"text-[10px] font-bold text-gray-500 uppercase tracking-widest mb-2",children:"Sync Strategy"}),d.jsx("div",{className:"space-y-1",children:gf.map(q=>d.jsxs("div",{onClick:()=>tl(q),className:`group flex items-center justify-between p-2 rounded-lg cursor-pointer transition-all border ${S===q.value?"bg-white/10 border-white/10 shadow-sm":"hover:bg-white/5 border-transparent"}`,children:[d.jsxs("div",{className:"flex items-center space-x-3 overflow-hidden",children:[d.jsx(q.icon,{size:18,className:q.color}),d.jsx("span",{className:`text-sm font-medium truncate ${S===q.value?"text-white":"text-gray-300 group-hover:text-white"}`,children:q.label})]}),d.jsxs("div",{className:"flex items-center space-x-2",children:[d.jsxs("div",{className:"relative group/tooltip",children:[d.jsx(Xm,{size:14,className:"text-gray-600 hover:text-gray-400 transition-colors"}),d.jsx("div",{className:"absolute right-0 bottom-full mb-2 w-48 p-2.5 bg-gray-900 text-xs text-gray-300 rounded-lg shadow-xl border border-gray-700 pointer-events-none opacity-0 group-hover/tooltip:opacity-100 transition-opacity z-50",children:q.description})]}),S===q.value&&d.jsx(Um,{size:14,className:"text-plex-orange",strokeWidth:3})]})]},q.value))})]}),d.jsxs("div",{className:"p-4 bg-gray-900/40",children:[d.jsxs("div",{className:"flex items-center justify-between mb-3",children:[d.jsx("h3",{className:"text-[10px] font-bold text-gray-500 uppercase tracking-widest",children:"Regex Rules"}),ll.length===0&&d.jsx("button",{onClick:El,className:"p-1 rounded bg-gray-700/50 hover:bg-gray-600 text-gray-400 hover:text-white transition-colors",title:"Add Rule",children:d.jsx(R0,{size:14})})]}),d.jsx("div",{className:"space-y-2 mb-4 max-h-52 overflow-y-auto pr-1 custom-scrollbar",children:ll.length===0?d.jsx("div",{className:"text-xs text-gray-600 italic text-center py-4 border border-dashed border-gray-700/50 rounded-lg",children:"No regex replacements configured."}):ll.map(q=>d.jsxs("div",{className:"flex items-center space-x-2 animate-in slide-in-from-left-2 duration-200",children:[d.jsx("div",{className:"flex-1 min-w-0",children:d.jsx("input",{type:"text",placeholder:"Regex Pattern",value:q.pattern,onChange:K=>xl(q.id,"pattern",K.target.value),className:`w-full bg-gray-900/80 border rounded-md px-2.5 py-1.5 text-xs text-gray-200 focus:outline-none focus:ring-1 transition-all placeholder-gray-600 + ${!q.pattern&&z?"border-red-500/30 focus:border-red-500":"border-gray-700 focus:border-plex-orange"}`})}),d.jsx("div",{className:"flex-none text-gray-600",children:d.jsx(G0,{size:12})}),d.jsx("div",{className:"flex-1 min-w-0",children:d.jsx("input",{type:"text",placeholder:"Replacement",value:q.replacement,onChange:K=>xl(q.id,"replacement",K.target.value),className:"w-full bg-gray-900/80 border border-gray-700 rounded-md px-2.5 py-1.5 text-xs text-gray-200 focus:outline-none focus:border-plex-orange focus:ring-1 focus:ring-plex-orange transition-all placeholder-gray-600"})}),d.jsx("button",{onClick:()=>_l(q.id),className:"text-gray-600 hover:text-red-400 p-1.5 hover:bg-red-500/10 rounded transition-colors",title:"Delete Rule",children:d.jsx(S1,{size:14})})]},q.id))}),d.jsxs("div",{className:"space-y-3 pt-3 border-t border-white/5",children:[ll.length>0&&d.jsx("div",{className:"flex justify-center",children:d.jsxs("button",{onClick:El,className:"flex items-center space-x-1.5 text-xs text-plex-orange hover:text-yellow-400 transition-colors opacity-80 hover:opacity-100",children:[d.jsx(R0,{size:12}),d.jsx("span",{className:"font-medium",children:"Add Rule"})]})}),d.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[d.jsxs("button",{onClick:$l,disabled:!z,className:`flex items-center justify-center space-x-2 py-1.5 rounded-lg text-xs font-medium border transition-all + ${z?"bg-gray-800 border-gray-600 text-gray-300 hover:bg-gray-700 hover:text-white":"bg-transparent border-gray-800 text-gray-600 cursor-not-allowed"}`,children:[d.jsx(o1,{size:14}),d.jsx("span",{children:"Revert"})]}),d.jsxs("button",{onClick:Nl,disabled:!z,className:`flex items-center justify-center space-x-2 py-1.5 rounded-lg text-xs font-bold border transition-all + ${z?"bg-plex-orange border-plex-orange text-gray-900 hover:bg-yellow-500 shadow-lg shadow-plex-orange/10":"bg-gray-800/50 border-gray-800 text-gray-600 cursor-not-allowed"}`,children:[d.jsx(d1,{size:14}),d.jsx("span",{children:"Save Changes"})]})]})]})]})]})]})},N1=({isOpen:S,onClose:_,onConnectSuccess:H,onShowMessage:y})=>{const[M,B]=J.useState({protocol:"http",address:"",port:"32400",token:"",username:"",password:""}),[F,ll]=J.useState(!1),[O,z]=J.useState(null),[X,C]=J.useState(!1),[tl,El]=J.useState(null),[_l,xl]=J.useState([]),[$l,Nl]=J.useState("");if(J.useEffect(()=>{S&&(z(null),El(null),xl([]),Nl(""))},[S]),!S)return null;const q=fl=>{const{name:Tl,value:Dl}=fl.target;B(Al=>({...Al,[Tl]:Dl}))},K=async fl=>{const Tl=fl.target.value;Nl(Tl);const Dl=_l.find(Al=>Al.id===Tl);if(Dl&&tl){const Al={...tl,libraryName:Dl.title};El(Al),H(Al),y(`Library switched to ${Dl.title}`),await Rt.selectLibrary(Dl.title)}},nl=M.token.trim().length>0,Ql=nl?"bg-gray-700/50 text-gray-500 line-through decoration-gray-500 cursor-not-allowed border-gray-700":"bg-gray-800 text-gray-100 border-gray-600 focus:border-plex-orange focus:ring-1 focus:ring-plex-orange",bl=async fl=>{fl.preventDefault(),z(null),ll(!0);const Tl=await Rt.connectToPlex(M);if(ll(!1),Tl.status==="success"&&Tl.data){B(Ol=>({...Ol,token:Tl.data.token,username:"",password:""}));const Dl=Tl.data.serverInfo;El(Dl),y(`Successfully connected to ${Dl.name||"Plex Server"}`);const Al=Dl.libraries||[];if(xl(Al),Al.length>0){const Ol=Al[0];Nl(Ol.id),await Rt.selectLibrary(Ol.title),H({...Dl,libraryName:Ol.title})}else H(Dl)}else z(Tl.message||"Connection failed")},L=!!tl;return d.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm",children:d.jsxs("div",{className:"bg-gray-900 border border-gray-700 rounded-xl shadow-2xl w-full max-w-md overflow-hidden animate-in fade-in zoom-in duration-200",children:[d.jsxs("div",{className:"px-6 py-4 bg-gray-800 border-b border-gray-700 flex items-center justify-between",children:[d.jsxs("h3",{className:"text-lg font-semibold text-white flex items-center gap-2",children:[d.jsx(Sf,{size:18,className:L?"text-green-400":"text-plex-orange"}),L?"Server Connected":"Connect Plex Server"]}),d.jsx("button",{onClick:_,className:"text-gray-400 hover:text-white transition-colors",children:d.jsx(X0,{size:20})})]}),d.jsxs("div",{className:"p-6",children:[d.jsxs("form",{onSubmit:bl,className:"space-y-4",children:[O&&d.jsx("div",{className:"p-3 bg-red-500/10 border border-red-500/20 text-red-400 text-xs rounded-md",children:O}),d.jsxs("div",{className:"space-y-3",children:[d.jsx("label",{className:"text-xs font-semibold text-gray-400 uppercase tracking-wider",children:"Server Details"}),d.jsxs("div",{className:"grid grid-cols-4 gap-3",children:[d.jsx("div",{className:"col-span-1",children:d.jsxs("select",{name:"protocol",value:M.protocol,onChange:q,disabled:L,className:`w-full h-10 px-2 bg-gray-800 border border-gray-600 rounded-md text-sm text-white focus:border-plex-orange focus:outline-none ${L?"opacity-60 cursor-not-allowed":""}`,children:[d.jsx("option",{value:"http",children:"HTTP"}),d.jsx("option",{value:"https",children:"HTTPS"})]})}),d.jsx("div",{className:"col-span-3",children:d.jsxs("div",{className:"relative",children:[d.jsx("div",{className:"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none",children:d.jsx(l1,{size:14,className:"text-gray-500"})}),d.jsx("input",{type:"text",name:"address",required:!0,disabled:L,placeholder:"IP Address or Domain",value:M.address,onChange:q,className:`w-full h-10 pl-9 pr-3 bg-gray-800 border border-gray-600 rounded-md text-sm text-white placeholder-gray-500 focus:border-plex-orange focus:outline-none focus:ring-1 focus:ring-plex-orange transition-all ${L?"opacity-60 cursor-not-allowed":""}`})]})})]}),d.jsx("div",{children:d.jsx("input",{type:"text",name:"port",disabled:L,placeholder:"Port (e.g. 32400)",value:M.port,onChange:q,className:`w-full h-10 px-3 bg-gray-800 border border-gray-600 rounded-md text-sm text-white placeholder-gray-500 focus:border-plex-orange focus:outline-none focus:ring-1 focus:ring-plex-orange transition-all ${L?"opacity-60 cursor-not-allowed":""}`})})]}),d.jsx("div",{className:"h-px bg-gray-800 my-4"}),d.jsxs("div",{className:"space-y-3",children:[d.jsx("label",{className:"text-xs font-semibold text-gray-400 uppercase tracking-wider",children:"Authentication"}),d.jsxs("div",{className:"relative",children:[d.jsx("div",{className:"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none",children:d.jsx(e1,{size:14,className:"text-plex-orange"})}),d.jsx("input",{type:"text",name:"token",disabled:L,placeholder:"X-Plex-Token (Optional)",value:M.token,onChange:q,className:`w-full h-10 pl-9 pr-3 bg-gray-800 border border-gray-600 rounded-md text-sm text-white placeholder-gray-500 focus:border-plex-orange focus:outline-none focus:ring-1 focus:ring-plex-orange transition-all font-mono ${L?"opacity-60 cursor-not-allowed":""}`})]}),!L&&d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"text-center text-[10px] text-gray-500 uppercase tracking-widest font-semibold py-1",children:"— OR —"}),d.jsxs("div",{className:"relative",children:[d.jsx("div",{className:"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none",children:d.jsx(x1,{size:14,className:nl?"text-gray-600":"text-gray-400"})}),d.jsx("input",{type:"text",name:"username",disabled:nl,placeholder:"Username / Email",value:M.username,onChange:q,className:`w-full h-10 pl-9 pr-3 rounded-md text-sm transition-all focus:outline-none ${Ql}`})]}),d.jsxs("div",{className:"relative",children:[d.jsx("div",{className:"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none",children:d.jsx(c1,{size:14,className:nl?"text-gray-600":"text-gray-400"})}),d.jsx("input",{type:X?"text":"password",name:"password",disabled:nl,placeholder:"Password",value:M.password,onChange:q,className:`w-full h-10 pl-9 pr-10 rounded-md text-sm transition-all focus:outline-none ${Ql}`}),d.jsx("button",{type:"button",disabled:nl,onClick:()=>C(!X),className:`absolute inset-y-0 right-0 pr-3 flex items-center ${nl?"cursor-not-allowed opacity-50":"cursor-pointer text-gray-400 hover:text-white"}`,children:X?d.jsx($m,{size:14}):d.jsx(Fm,{size:14})})]}),nl&&d.jsx("p",{className:"text-[10px] text-yellow-500/80 italic text-center",children:"Credential login disabled when token is present."})]})]}),L?d.jsx("div",{className:"mt-2 p-2 bg-green-500/10 border border-green-500/20 rounded-lg text-center",children:d.jsxs("p",{className:"text-green-400 text-sm font-semibold flex items-center justify-center gap-2",children:[d.jsx(Lm,{size:16}),"Connected Successfully"]})}):d.jsx("button",{type:"submit",disabled:F,className:`w-full mt-4 py-2.5 rounded-lg text-sm font-bold text-gray-900 transition-all shadow-lg + ${F?"bg-gray-600 cursor-wait":"bg-plex-orange hover:bg-yellow-500 active:scale-[0.98] shadow-plex-orange/20"}`,children:F?"Connecting...":"Connect Server"})]}),L&&_l.length>0&&d.jsxs("div",{className:"mt-6 pt-5 border-t border-gray-700 animate-in slide-in-from-top-2 fade-in",children:[d.jsx("label",{className:"text-xs font-semibold text-gray-400 uppercase tracking-wider block mb-2",children:"Select Library to Sync"}),d.jsxs("div",{className:"relative",children:[d.jsx("div",{className:"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none",children:d.jsx(u1,{size:14,className:"text-plex-orange"})}),d.jsx("select",{value:$l,onChange:K,className:"w-full h-10 pl-9 pr-3 bg-gray-800 border border-gray-600 rounded-md text-sm text-white focus:border-plex-orange focus:outline-none focus:ring-1 focus:ring-plex-orange appearance-none cursor-pointer hover:bg-gray-700/50 transition-colors",children:_l.map(fl=>d.jsx("option",{value:fl.id,children:fl.title},fl.id))}),d.jsx("div",{className:"absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none",children:d.jsx(L0,{size:14,className:"text-gray-500"})})]}),d.jsx("div",{className:"mt-6 flex justify-end",children:d.jsx("button",{onClick:_,className:"px-6 py-2 bg-gray-700 hover:bg-gray-600 text-white text-sm font-medium rounded-lg transition-colors border border-gray-600 hover:border-gray-500",children:"Done"})})]})]})]})})},O1=()=>{const[S,_]=J.useState([]),[H,y]=J.useState([]),[M,B]=J.useState(void 0),[F,ll]=J.useState(!1),[O,z]=J.useState(!1),[X,C]=J.useState(6e4),[tl,El]=J.useState(!1),[_l,xl]=J.useState(ee.LOCAL_OVERWRITE),[$l,Nl]=J.useState([]),[q,K]=J.useState([]),nl=J.useRef({}),Ql=m=>{K(A=>A.filter(D=>D.id!==m)),nl.current[m]&&(clearTimeout(nl.current[m]),delete nl.current[m])},bl=m=>{const A=Date.now(),D={id:A,message:m,exiting:!1,entering:!0};K(el=>[...el.map(T=>({...T,exiting:!0,entering:!1})),D]);const ul=setTimeout(()=>{K(el=>el.map(o=>o.id===A?{...o,exiting:!0}:o))},3e3);nl.current[A]=ul};J.useEffect(()=>{const m=q.filter(A=>A.entering).map(A=>A.id);if(m.length>0){let A,D;return A=requestAnimationFrame(()=>{D=requestAnimationFrame(()=>{K(ul=>ul.map(el=>m.includes(el.id)?{...el,entering:!1}:el))})}),()=>{cancelAnimationFrame(A),cancelAnimationFrame(D)}}},[q]),J.useEffect(()=>{q.filter(A=>A.exiting).forEach(A=>{nl.current[`remove-${A.id}`]||(nl.current[`remove-${A.id}`]=setTimeout(()=>{Ql(A.id),delete nl.current[`remove-${A.id}`]},300))})},[q]);const L=J.useCallback(async()=>{ll(!0);const m=await Rt.getPlaylists(Je.LOCAL);m.status==="success"&&_(m.data),ll(!1)},[]),fl=J.useCallback(async()=>{z(!0);const m=await Rt.getPlaylists(Je.CLOUD);m.status==="success"?y(m.data):y([]);const A=await Rt.getServerStatus();A.status==="success"?B(A.data):B({isConnected:!1}),z(!1)},[]);J.useEffect(()=>{(async()=>{const[A,D]=await Promise.all([Rt.getSettings(),Rt.getUiConfig()]);if(A.status==="success"&&(xl(Tl(A.data.syncStrategy)),A.data.regexRules&&Nl(A.data.regexRules)),D.status==="success"){const ul=Math.max(1e4,(D.data.statusCheckIntervalSeconds||60)*1e3);C(ul)}L(),fl()})()},[L,fl]),J.useEffect(()=>{const m=window.setInterval(()=>{fl()},X);return()=>window.clearInterval(m)},[fl,X]);const Tl=m=>m&&Object.values(ee).includes(m)?m:ee.LOCAL_OVERWRITE,Dl=async(m,A)=>{xl(m),(await Rt.saveStrategy(m)).status==="success"?bl(`Selected strategy "${A}" has been saved.`):bl("Failed to save strategy to server.")},Al=async m=>{const A=await Rt.saveRegexRules(m);A.status==="success"?(Nl(A.data),bl("Regex preprocessing rules have been saved.")):bl(A.message||"Failed to save regex rules.")},Ol=m=>{B(m),fl()},Ht=m=>m.exiting||m.entering?{opacity:0,transform:"translateY(-40px) scale(0.95)"}:{opacity:1,transform:"translateY(0) scale(1)"},_t=()=>"absolute top-2 flex items-center space-x-2 px-4 py-2 rounded-full shadow-lg border text-sm font-medium pointer-events-auto bg-gray-800 text-plex-orange border-plex-orange/30 transition-all duration-300 ease-out origin-top z-50 backdrop-blur-md",Wl=M==null?void 0:M.isConnected;return d.jsxs("div",{className:"min-h-screen flex flex-col bg-gray-900 text-gray-100 font-sans overflow-hidden bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-gray-800 via-gray-900 to-black",children:[d.jsx("header",{className:"flex-none bg-gray-800/80 border-b border-white/5 shadow-md z-20 relative backdrop-blur-md",children:d.jsxs("div",{className:"max-w-7xl mx-auto px-4 md:px-6 h-16 flex items-center justify-between",children:[d.jsxs("div",{className:"flex items-center space-x-3",children:[d.jsx("div",{className:"bg-gradient-to-br from-plex-orange to-yellow-600 p-1.5 rounded-lg text-gray-900 shadow-lg shadow-plex-orange/20",children:d.jsx(Dm,{size:24,strokeWidth:2.5})}),d.jsxs("h1",{className:"text-xl font-bold tracking-tight text-white",children:["Plex",d.jsx("span",{className:"text-plex-orange",children:"Sync"})]})]}),d.jsx("button",{onClick:()=>El(!0),className:`flex items-center justify-center w-9 h-9 rounded-full border transition-all duration-300 hover:scale-105 active:scale-95 shadow-md ${Wl?"bg-green-500/10 border-green-500/50 text-green-400 hover:bg-green-500/20 hover:shadow-green-500/20":"bg-red-500/10 border-red-500/50 text-red-400 hover:bg-red-500/20 hover:shadow-red-500/20"}`,title:Wl?"Connected to Plex":"Disconnected",children:Wl?d.jsx(Sf,{size:18}):d.jsx(m1,{size:18})})]})}),d.jsx("div",{className:"fixed top-20 left-0 right-0 flex justify-center h-0 overflow-visible z-[100] pointer-events-none",children:q.map(m=>d.jsxs("div",{className:_t(),style:Ht(m),children:[d.jsx(g1,{size:16}),d.jsx("span",{children:m.message}),d.jsx("button",{onClick:()=>K(A=>A.map(D=>D.id===m.id?{...D,exiting:!0}:D)),className:"ml-2 hover:text-white transition-colors",children:d.jsx(X0,{size:14})})]},m.id))}),d.jsx("main",{className:"flex-1 overflow-hidden relative z-10",children:d.jsxs("div",{className:"absolute inset-0 flex flex-col md:flex-row max-w-7xl mx-auto p-4 md:p-6 gap-3 md:gap-6",children:[d.jsx("div",{className:"flex-1 min-h-0 h-full w-full",children:d.jsx(q0,{type:Je.LOCAL,playlists:S,isLoading:F,onRefresh:L})}),d.jsx("div",{className:`absolute + z-30 + /* Mobile Positioning: Center Vertically, Anchored Right */ + top-1/2 right-[52px] transform translate-x-1/2 -translate-y-1/2 + + /* Desktop Positioning: Center Horizontally, Anchored Top */ + md:top-[64px] md:right-auto md:left-1/2 md:transform md:-translate-x-1/2 md:-translate-y-1/2`,children:d.jsx(_1,{currentStrategy:_l,onSelect:Dl,savedRegexReplacements:$l,onSaveRegex:Al})}),d.jsx("div",{className:"flex-1 min-h-0 h-full w-full",children:d.jsx(q0,{type:Je.CLOUD,playlists:H,isLoading:O,onRefresh:fl,serverInfo:M})})]})}),d.jsx("footer",{className:"flex-none py-4 text-center text-xs text-gray-600 border-t border-white/5 bg-gray-900/50 backdrop-blur",children:d.jsxs("p",{children:["© ",new Date().getFullYear()," PlexSync Manager. Connected to Docker backend."]})}),d.jsx(N1,{isOpen:tl,onClose:()=>El(!1),onConnectSuccess:Ol,onShowMessage:bl})]})},Q0=document.getElementById("root");if(!Q0)throw new Error("Could not find root element to mount to");const M1=Em.createRoot(Q0);M1.render(d.jsx(hm.StrictMode,{children:d.jsx(O1,{})})); diff --git a/app/static/frontend/index.html b/app/static/frontend/index.html new file mode 100644 index 0000000..9af5767 --- /dev/null +++ b/app/static/frontend/index.html @@ -0,0 +1,56 @@ + + + + + + PlexSync Manager + + + + + + + + +
+ + \ No newline at end of file diff --git a/app/utils/config.py b/app/utils/config.py index 8ee68b9..2ba326b 100644 --- a/app/utils/config.py +++ b/app/utils/config.py @@ -17,6 +17,9 @@ class ServerConfig: self.port = "32400" self.library_name = "" self.path_rules: list[dict[str, str]] = [] + # 新增:本地播放列表目录和默认同步策略(用于新的前端界面) + self.local_playlist_dir = os.getenv("LOCAL_PLAYLIST_DIR", "playlist") + self.sync_strategy = "LOCAL_OVERWRITE" self.load() def load(self) -> None: @@ -41,6 +44,10 @@ class ServerConfig: self.port = config.get("server_port", "32400") self.library_name = config.get("library_name", "") self.path_rules = config.get("path_rules", []) or [] + self.local_playlist_dir = config.get( + "local_playlist_dir", self.local_playlist_dir + ) + self.sync_strategy = config.get("sync_strategy", self.sync_strategy) logger.info(f"Server config loaded: {self.__dict__}") def save(self): @@ -52,6 +59,8 @@ class ServerConfig: "server_port": self.port, "library_name": self.library_name, "path_rules": self.path_rules, + "local_playlist_dir": self.local_playlist_dir, + "sync_strategy": self.sync_strategy, } with open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(config, f, indent=4, ensure_ascii=False) @@ -82,6 +91,13 @@ class ServerConfig: def set_path_rules(self, path_rules: list[dict[str, str]]) -> None: self.path_rules = path_rules or [] + def set_local_playlist_dir(self, playlist_dir: str) -> None: + if playlist_dir: + self.local_playlist_dir = playlist_dir + + def set_sync_strategy(self, sync_strategy: str) -> None: + self.sync_strategy = sync_strategy + def set_and_save_config( self, theme: str = None, @@ -91,6 +107,8 @@ class ServerConfig: port: str = None, library_name: str | None = None, path_rules: list[dict[str, str]] | None = None, + local_playlist_dir: str | None = None, + sync_strategy: str | None = None, ) -> None: if theme is not None: self.set_theme(theme) @@ -106,6 +124,10 @@ class ServerConfig: self.set_library(library_name) if path_rules is not None: self.set_path_rules(path_rules) + if local_playlist_dir is not None: + self.set_local_playlist_dir(local_playlist_dir) + if sync_strategy is not None: + self.set_sync_strategy(sync_strategy) self.save() diff --git a/app/utils/local_playlist.py b/app/utils/local_playlist.py index 20c5d82..9a682d3 100644 --- a/app/utils/local_playlist.py +++ b/app/utils/local_playlist.py @@ -1,5 +1,6 @@ import os from typing import List +from datetime import datetime from app.utils.logger import logger def load_local_playlist(playlist_path: str) -> List[str]: @@ -42,7 +43,9 @@ def scan_local_playlists(base_path: str) -> list[dict]: base_path: Directory that contains playlist files. Returns: - A list of dictionaries with ``name`` and ``track_count`` keys. + A list of dictionaries with ``name`` and ``track_count`` keys, and + additional metadata (``path`` and ``last_modified``) for richer API + responses. """ playlists: list[dict] = [] @@ -61,7 +64,15 @@ def scan_local_playlists(base_path: str) -> list[dict]: if not entry.name.lower().endswith((".m3u", ".m3u8")): continue tracks = load_local_playlist(entry.path) - playlists.append({"name": entry.name, "track_count": len(tracks)}) + last_modified = datetime.fromtimestamp(entry.stat().st_mtime) + playlists.append( + { + "name": entry.name, + "track_count": len(tracks), + "path": entry.path, + "last_modified": last_modified.isoformat(), + } + ) playlists.sort(key=lambda item: item["name"].lower()) logger.info(f"Found {len(playlists)} playlists under {absolute_path}.") diff --git a/sample-front-end/App.tsx b/sample-front-end/App.tsx index d05aef7..ac961af 100644 --- a/sample-front-end/App.tsx +++ b/sample-front-end/App.tsx @@ -18,9 +18,10 @@ const App: React.FC = () => { const [localPlaylists, setLocalPlaylists] = useState([]); const [cloudPlaylists, setCloudPlaylists] = useState([]); const [cloudServerInfo, setCloudServerInfo] = useState(undefined); - + const [loadingLocal, setLoadingLocal] = useState(false); const [loadingCloud, setLoadingCloud] = useState(false); + const [statusIntervalMs, setStatusIntervalMs] = useState(60000); // Connection Modal State const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); @@ -115,12 +116,16 @@ const App: React.FC = () => { const playlistResult = await apiService.getPlaylists(ServerType.CLOUD); if (playlistResult.status === 'success') { setCloudPlaylists(playlistResult.data); + } else { + setCloudPlaylists([]); } - + // Fetch server info const infoResult = await apiService.getServerStatus(); if (infoResult.status === 'success') { setCloudServerInfo(infoResult.data); + } else { + setCloudServerInfo({ isConnected: false }); } setLoadingCloud(false); @@ -128,20 +133,68 @@ const App: React.FC = () => { // Initial Load useEffect(() => { - refreshLocal(); - refreshCloud(); + const loadSettings = async () => { + const [settings, uiConfig] = await Promise.all([ + apiService.getSettings(), + apiService.getUiConfig() + ]); + + if (settings.status === 'success') { + setCurrentStrategy(normalizeStrategy(settings.data.syncStrategy)); + if (settings.data.regexRules) { + setRegexReplacements(settings.data.regexRules); + } + } + + if (uiConfig.status === 'success') { + const ms = Math.max(10000, (uiConfig.data.statusCheckIntervalSeconds || 60) * 1000); + setStatusIntervalMs(ms); + } + + refreshLocal(); + refreshCloud(); + }; + + loadSettings(); }, [refreshLocal, refreshCloud]); + // Periodically check cloud connection status to avoid stale UI loops + useEffect(() => { + const timer = window.setInterval(() => { + refreshCloud(); + }, statusIntervalMs); + + return () => window.clearInterval(timer); + }, [refreshCloud, statusIntervalMs]); + + const normalizeStrategy = (value?: string): SyncStrategy => { + if (!value) return SyncStrategy.LOCAL_OVERWRITE; + const values = Object.values(SyncStrategy); + return values.includes(value as SyncStrategy) + ? (value as SyncStrategy) + : SyncStrategy.LOCAL_OVERWRITE; + }; + // Handle Strategy Change - const handleStrategyChange = (strategy: SyncStrategy, label: string) => { + const handleStrategyChange = async (strategy: SyncStrategy, label: string) => { setCurrentStrategy(strategy); - addToast(`Selected strategy "${label}" has been saved.`); + const result = await apiService.saveStrategy(strategy); + if (result.status === 'success') { + addToast(`Selected strategy "${label}" has been saved.`); + } else { + addToast('Failed to save strategy to server.'); + } }; // Handle Regex Save - const handleSaveRegex = (replacements: RegexReplacement[]) => { - setRegexReplacements(replacements); - addToast('Regex preprocessing rules have been saved.'); + const handleSaveRegex = async (replacements: RegexReplacement[]) => { + const result = await apiService.saveRegexRules(replacements); + if (result.status === 'success') { + setRegexReplacements(result.data); + addToast('Regex preprocessing rules have been saved.'); + } else { + addToast(result.message || 'Failed to save regex rules.'); + } }; const handleConnectSuccess = (serverInfo: PlexServerConnection) => { diff --git a/sample-front-end/components/ConnectionModal.tsx b/sample-front-end/components/ConnectionModal.tsx index f2bb243..8d7e23c 100644 --- a/sample-front-end/components/ConnectionModal.tsx +++ b/sample-front-end/components/ConnectionModal.tsx @@ -47,10 +47,10 @@ const ConnectionModal: React.FC = ({ isOpen, onClose, onCo setFormData(prev => ({ ...prev, [name]: value })); }; - const handleLibraryChange = (e: React.ChangeEvent) => { + const handleLibraryChange = async (e: React.ChangeEvent) => { const newId = e.target.value; setSelectedLibraryId(newId); - + const lib = libraries.find(l => l.id === newId); if (lib && connectedServerInfo) { const updatedInfo = { ...connectedServerInfo, libraryName: lib.title }; @@ -59,6 +59,7 @@ const ConnectionModal: React.FC = ({ isOpen, onClose, onCo onConnectSuccess(updatedInfo); // Show toast onShowMessage(`Library switched to ${lib.title}`); + await apiService.selectLibrary(lib.title); } }; @@ -100,6 +101,7 @@ const ConnectionModal: React.FC = ({ isOpen, onClose, onCo const defaultLib = libs[0]; setSelectedLibraryId(defaultLib.id); // Pass connection info back with default library name explicitly set (though mock already does it) + await apiService.selectLibrary(defaultLib.title); onConnectSuccess({ ...info, libraryName: defaultLib.title diff --git a/sample-front-end/index.html b/sample-front-end/index.html index 313a4b5..bbab53a 100644 --- a/sample-front-end/index.html +++ b/sample-front-end/index.html @@ -47,6 +47,7 @@ } } +
diff --git a/sample-front-end/package-lock.json b/sample-front-end/package-lock.json new file mode 100644 index 0000000..1fbe3fc --- /dev/null +++ b/sample-front-end/package-lock.json @@ -0,0 +1,1771 @@ +{ + "name": "plexsync-manager", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plexsync-manager", + "version": "0.0.0", + "dependencies": { + "lucide-react": "^0.555.0", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", + "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz", + "integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.47", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", + "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.262", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz", + "integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.555.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.555.0.tgz", + "integrity": "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/sample-front-end/services/api.ts b/sample-front-end/services/api.ts index 0684670..8c92dc0 100644 --- a/sample-front-end/services/api.ts +++ b/sample-front-end/services/api.ts @@ -1,98 +1,90 @@ -import { Playlist, ServerType, ApiResponse, PlexServerConnection, PlexConnectionSettings, PlexLibrary } from '../types'; -import { MOCK_LOCAL_PLAYLISTS, MOCK_CLOUD_PLAYLISTS } from './mockData'; +import { Playlist, ServerType, ApiResponse, PlexServerConnection, PlexConnectionSettings, RegexReplacement, UiConfig, SyncSettings } from '../types'; -const SIMULATE_DELAY_MS = 800; +const API_PREFIX = '/api'; -// Mock available libraries on a server -const MOCK_LIBRARIES: PlexLibrary[] = [ - { id: 'lib1', title: 'Music (Flac)', type: 'artist' }, - { id: 'lib2', title: 'MP3 Collection', type: 'artist' }, - { id: 'lib3', title: 'Soundtracks', type: 'artist' }, - { id: 'lib4', title: 'Audiobooks', type: 'artist' } -]; - -// Helper to simulate network request or call actual API -const fetchPlaylists = async (type: ServerType): Promise => { - // In a real Docker environment with FastAPI, you would do: - // const response = await fetch(`/api/playlists/${type.toLowerCase()}`); - // const data = await response.json(); - // return data; - - // Mocking for UI demonstration - return new Promise((resolve) => { - setTimeout(() => { - if (type === ServerType.LOCAL) { - resolve([...MOCK_LOCAL_PLAYLISTS]); - } else { - resolve([...MOCK_CLOUD_PLAYLISTS]); - } - }, SIMULATE_DELAY_MS); - }); +const parseJson = async (response: Response): Promise> => { + try { + const data = await response.json(); + return data as ApiResponse; + } catch (error) { + console.error('Failed to parse API response', error); + return { data: {} as T, status: 'error', message: 'Invalid response from server' }; + } }; -const fetchServerStatus = async (): Promise => { - // Mocking server status - return new Promise((resolve) => { - setTimeout(() => { - // 90% chance of success for demo - const isSuccess = Math.random() > 0.1; - if (isSuccess) { - resolve({ - isConnected: true, - name: 'Home Media Server', - ip: '192.168.1.105', - port: 32400, - libraryName: 'Music (Flac)' - }); - } else { - resolve({ - isConnected: false - }); - } - }, SIMULATE_DELAY_MS); - }); -}; - -const authenticatePlex = async (settings: PlexConnectionSettings): Promise<{ token: string, serverInfo: PlexServerConnection }> => { - return new Promise((resolve, reject) => { - setTimeout(() => { - // Simulate validation - if (!settings.address) { - reject(new Error("Server address is required")); - return; - } - - // If user provided username/password, mock a token generation - let token = settings.token; - if (!token && settings.username && settings.password) { - token = "MOCK_TOKEN_XYZ_999"; - } else if (!token) { - reject(new Error("Token or Username/Password required")); - return; - } - - // Success response with libraries - resolve({ - token: token, - serverInfo: { - isConnected: true, - name: 'My Plex Server', - ip: settings.address, - port: parseInt(settings.port) || 32400, - libraryName: MOCK_LIBRARIES[0].title, // Default to first library - libraries: MOCK_LIBRARIES - } - }); - }, 1500); - }); -} +const mapServerType = (type: ServerType) => type === ServerType.LOCAL ? 'local' : 'cloud'; export const apiService = { + getUiConfig: async (): Promise> => { + try { + const response = await fetch(`${API_PREFIX}/ui-config`); + const result = await parseJson(response); + if (!response.ok || result.status !== 'success') { + return { data: { statusCheckIntervalSeconds: 60 }, status: 'error', message: result.message || '无法获取前端配置' }; + } + return result; + } catch (error) { + return { data: { statusCheckIntervalSeconds: 60 }, status: 'error', message: '无法获取前端配置' }; + } + }, + + getSettings: async (): Promise> => { + try { + const response = await fetch(`${API_PREFIX}/settings`); + const result = await parseJson(response); + if (!response.ok || result.status !== 'success') { + return { data: { syncStrategy: 'LOCAL_OVERWRITE', regexRules: [] }, status: 'error', message: result.message || '无法获取设置' }; + } + return result; + } catch (error) { + return { data: { syncStrategy: 'LOCAL_OVERWRITE', regexRules: [] }, status: 'error', message: '无法获取设置' }; + } + }, + + saveStrategy: async (strategy: string): Promise> => { + try { + const response = await fetch(`${API_PREFIX}/settings/strategy`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ strategy }) + }); + return await parseJson(response); + } catch (error) { + return { data: { syncStrategy: strategy, regexRules: [] }, status: 'error', message: '无法保存同步策略' }; + } + }, + + getRegexRules: async (): Promise> => { + try { + const response = await fetch(`${API_PREFIX}/regex-rules`); + return await parseJson(response); + } catch (error) { + return { data: [], status: 'error', message: '无法获取正则规则' }; + } + }, + + saveRegexRules: async (rules: RegexReplacement[]): Promise> => { + try { + const response = await fetch(`${API_PREFIX}/regex-rules`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(rules) + }); + return await parseJson(response); + } catch (error) { + return { data: [], status: 'error', message: '无法保存正则规则' }; + } + }, + getPlaylists: async (serverType: ServerType): Promise> => { try { - const data = await fetchPlaylists(serverType); - return { data, status: 'success' }; + const response = await fetch(`${API_PREFIX}/playlists/${mapServerType(serverType)}`); + const result = await parseJson(response); + if (!response.ok || result.status !== 'success') { + return { data: [], status: 'error', message: result.message || 'Failed to fetch playlists' }; + } + return result; } catch (error) { console.error(`Error fetching ${serverType} playlists:`, error); return { data: [], status: 'error', message: 'Failed to fetch playlists' }; @@ -101,27 +93,48 @@ export const apiService = { getServerStatus: async (): Promise> => { try { - const data = await fetchServerStatus(); - return { data, status: 'success' }; + const response = await fetch(`${API_PREFIX}/server/status`); + const result = await parseJson(response); + if (!response.ok || result.status !== 'success') { + return { data: { isConnected: false }, status: 'error', message: result.message || 'Failed to connect to server' }; + } + return result; } catch (error) { - return { - data: { isConnected: false }, - status: 'error', - message: 'Failed to connect to server' + return { + data: { isConnected: false }, + status: 'error', + message: 'Failed to connect to server' }; } }, connectToPlex: async (settings: PlexConnectionSettings): Promise> => { try { - const data = await authenticatePlex(settings); - return { data, status: 'success', message: 'Connected successfully' }; + const response = await fetch(`${API_PREFIX}/server/connect`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(settings) + }); + return await parseJson<{ token: string, serverInfo: PlexServerConnection }>(response); } catch (error: any) { - return { - data: { token: '', serverInfo: { isConnected: false } }, - status: 'error', - message: error.message || 'Connection failed' + return { + data: { token: '', serverInfo: { isConnected: false } }, + status: 'error', + message: error?.message || 'Connection failed' }; } + }, + + selectLibrary: async (library: string): Promise> => { + try { + const response = await fetch(`${API_PREFIX}/server/library`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ library }) + }); + return await parseJson<{ libraryName: string }>(response); + } catch (error) { + return { data: { libraryName: library }, status: 'error', message: '无法切换媒体库' }; + } } }; diff --git a/sample-front-end/types.ts b/sample-front-end/types.ts index 923ba8a..b7ef2f9 100644 --- a/sample-front-end/types.ts +++ b/sample-front-end/types.ts @@ -61,4 +61,14 @@ export interface ApiResponse { data: T; status: 'success' | 'error'; message?: string; +} + +export interface UiConfig { + statusCheckIntervalSeconds: number; + localPlaylistDir?: string; +} + +export interface SyncSettings { + syncStrategy: string; + regexRules?: RegexReplacement[]; } \ No newline at end of file diff --git a/sample-front-end/vite.config.ts b/sample-front-end/vite.config.ts index ee5fb8d..7a927de 100644 --- a/sample-front-end/vite.config.ts +++ b/sample-front-end/vite.config.ts @@ -18,6 +18,11 @@ export default defineConfig(({ mode }) => { alias: { '@': path.resolve(__dirname, '.'), } + }, + base: '/', + build: { + outDir: path.resolve(__dirname, '../app/static/frontend'), + emptyOutDir: true, } }; });