diff --git a/.gitignore b/.gitignore
index 52615c4..7474dc1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,19 +1,5 @@
-## Dependencies
-**/node_modules/
-
-## Build output
-**/dist/
-
-## Cloudflare / Wrangler
-**/.wrangler/
-**/.cache/
-
-## Local env / secrets
-**/.dev.vars
-**/.env
-**/.env.*
-
-## OS / editor
-.DS_Store
-Thumbs.db
-.vscode/
+node_modules/
+.dev.vars
+frontend/dist
+frontend/dist-desktop
+.wrangler
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c2d6df9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,55 @@
+# 萌芽导航(sproutnav)
+
+单一 **Cloudflare Worker**:[`worker/src/index.ts`](worker/src/index.ts) 处理 `/api/*`,静态资源由 Wrangler **[assets]** 提供(Vite 构建输出 [`frontend/dist`](frontend/dist))。
+
+## 目录结构
+
+- **`worker/`**:导航 API(KV:`sites` / `categories`)、与静态资源回退(SPA)
+- **`frontend/`**:React + Vite PWA;[`frontend/src/config/site.ts`](frontend/src/config/site.ts) 可配置 `VITE_API_BASE`、站点文案、随机背景、`VITE_FAVICON_API_BASE`(默认 `https://favicon.smyhub.com/api/favicon?url=`)
+- **[`wrangler.toml`](wrangler.toml)**:`main`、`[[kv_namespaces]]`、`[vars]`(如 `ADMIN_PASSWORD`)、`[assets]` → `./frontend/dist`
+
+## 本地开发
+
+```bash
+# 仅前端(无同源 API 时需自行 mock 或填 VITE_API_BASE)
+cd frontend && npm install && npm run dev
+
+# Worker + 资源(需先 build 前端,或由 wrangler 触发)
+npm install
+npm run build:frontend
+npm run dev
+```
+
+## 构建产物
+
+`npm run build`(根目录或 `frontend/`)会连续构建两份前端:
+
+| 脚本 / 模式 | 输出目录 | `VITE_API_BASE` | 用途 |
+|-------------|----------|-----------------|------|
+| 默认 `vite build` | `frontend/dist` | 空(同源 `/api`) | Worker `[assets]` 线上部署 |
+| `--mode desktop` | `frontend/dist-desktop` | `https://nav.smyhub.com` | 桌面壳(Electron/Tauri 等)加载 |
+
+单独构建:`npm run build:web` / `npm run build:desktop`(在 `frontend/` 下)。
+
+桌面版使用 `HashRouter`、`base: './'`,且不注册 Service Worker。
+
+## 部署
+
+```bash
+npm install
+cd frontend && npm install && cd ..
+npm run deploy
+```
+
+`deploy` 仅打包并上传 **`frontend/dist`**(线上版);桌面版 `dist-desktop` 自行交给桌面工程。
+
+生产环境请在 Dashboard 或密文中覆盖 **`ADMIN_PASSWORD`**,勿提交真实口令。
+
+## KV 数据
+
+沿用 [`wrangler.toml`](wrangler.toml) 中 namespace `id` 即可续用原有 KV 数据。
+
+## 常见问题
+
+- **站点卡片图标**:由前端根据每条站点的 `url` 请求 favicon 服务,不经 Worker;可通过 `VITE_FAVICON_API_BASE` 覆盖默认基址。
+- **跨域 / 缓存**:若 API 已同源但仍见旧请求,尝试硬刷新或注销 Service Worker 后清除站点数据。
diff --git a/cf-nav-frontend/_redirects b/_redirects
similarity index 99%
rename from cf-nav-frontend/_redirects
rename to _redirects
index 350a976..8315bc1 100644
--- a/cf-nav-frontend/_redirects
+++ b/_redirects
@@ -1 +1 @@
-# 后台请直接访问 /admin.html?token=你的令牌(不要配置 /admin 或 /admin.html 的重定向,避免循环)
+# 后台请直接访问 /admin.html?token=你的令牌(不要配置 /admin 或 /admin.html 的重定向,避免循环)
diff --git a/cf-nav-backend/package.json b/cf-nav-backend/package.json
deleted file mode 100644
index 6a68eee..0000000
--- a/cf-nav-backend/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "cf-nav-backend",
- "version": "1.0.0",
- "private": true,
- "scripts": {
- "deploy": "wrangler deploy",
- "dev": "wrangler dev"
- },
- "devDependencies": {
- "wrangler": "^4.68.1"
- }
-}
diff --git a/cf-nav-backend/worker.js b/cf-nav-backend/worker.js
deleted file mode 100644
index 46c1ad9..0000000
--- a/cf-nav-backend/worker.js
+++ /dev/null
@@ -1,309 +0,0 @@
-// Cloudflare Worker - 仅 API 后端(前后端分离)
-// 部署到 cf-nav-backend,前端静态资源由 Cloudflare Pages 托管
-
-const corsHeaders = {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
-};
-
-function verifyAuth(request, env) {
- const auth = request.headers.get('Authorization');
- const token = (env.ADMIN_PASSWORD || env.ADMIN_TOKEN || '').trim();
- if (!token) return false;
- const prefix = 'Bearer ';
- if (!auth || !auth.startsWith(prefix)) return false;
- const provided = auth.slice(prefix.length).trim();
- return provided === token;
-}
-
-export default {
- async fetch(request, env) {
- if (request.method === 'OPTIONS') {
- return new Response(null, { headers: corsHeaders });
- }
-
- const url = new URL(request.url);
- const path = url.pathname;
-
- try {
- if (path === '/api/sites') {
- return handleSites(request, env, corsHeaders);
- }
- if (path === '/api/auth/check') {
- return handleAuthCheck(request, env, corsHeaders);
- }
- if (path.match(/^\/api\/sites\/[^/]+\/click$/)) {
- const id = path.split('/')[3];
- return handleSiteClick(request, env, id, corsHeaders);
- }
- if (path.startsWith('/api/sites/')) {
- const id = path.split('/')[3];
- return handleSite(request, env, id, corsHeaders);
- }
- if (path === '/api/categories') {
- return handleCategories(request, env, corsHeaders);
- }
- if (path.startsWith('/api/categories/')) {
- const name = decodeURIComponent(path.split('/')[3] || '');
- return handleCategory(request, env, name, corsHeaders);
- }
- if (path === '/api/favicon') {
- return handleFavicon(request, env, corsHeaders);
- }
-
- return new Response('Not Found', { status: 404 });
- } catch (error) {
- return new Response('Internal Server Error: ' + error.message, {
- status: 500,
- headers: corsHeaders,
- });
- }
- },
-};
-
-/** 校验管理员 token,用于前端进入后台时确认链接有效 */
-async function handleAuthCheck(request, env, corsHeaders) {
- if (request.method !== 'GET') {
- return new Response('Method not allowed', { status: 405, headers: corsHeaders });
- }
- const token = (env.ADMIN_PASSWORD || env.ADMIN_TOKEN || '').trim();
- if (!token) {
- return new Response(JSON.stringify({ ok: false }), {
- status: 401,
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- const auth = request.headers.get('Authorization');
- const prefix = 'Bearer ';
- if (!auth || !auth.startsWith(prefix)) {
- return new Response(JSON.stringify({ ok: false }), {
- status: 401,
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- const provided = auth.slice(prefix.length).trim();
- if (provided !== token) {
- return new Response(JSON.stringify({ ok: false }), {
- status: 401,
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- return new Response(JSON.stringify({ ok: true }), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
-}
-
-async function handleFavicon(request, env, corsHeaders) {
- const url = new URL(request.url);
- const domain = url.searchParams.get('domain');
- if (!domain) {
- return new Response('Missing domain parameter', { status: 400, headers: corsHeaders });
- }
- const faviconApi = env.FAVICON_API || env.FAVICON;
- if (faviconApi && faviconApi !== '') {
- const targetUrl = domain.startsWith('http') ? domain : `https://${domain}`;
- try {
- const res = await fetch(faviconApi + encodeURIComponent(targetUrl), {
- headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
- cf: { cacheTtl: 86400, cacheEverything: true },
- });
- if (res.ok) {
- return new Response(res.body, {
- status: res.status,
- headers: {
- ...corsHeaders,
- 'Content-Type': res.headers.get('Content-Type') || 'image/x-icon',
- 'Cache-Control': 'public, max-age=86400',
- },
- });
- }
- } catch (_) {
- /* 外置 API 失败时回退到内置源 */
- }
- }
- const faviconSources = [
- `https://www.google.com/s2/favicons?domain=${domain}&sz=64`,
- `https://icons.duckduckgo.com/ip3/${domain}.ico`,
- `https://favicon.api.shumengya.top/${domain}`,
- ];
- for (const source of faviconSources) {
- try {
- const response = await fetch(source, {
- headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
- cf: { cacheTtl: 86400, cacheEverything: true },
- });
- if (response.ok) {
- return new Response(response.body, {
- status: response.status,
- headers: {
- ...corsHeaders,
- 'Content-Type': response.headers.get('Content-Type') || 'image/x-icon',
- 'Cache-Control': 'public, max-age=86400',
- },
- });
- }
- } catch (_) {
- continue;
- }
- }
- return new Response('Favicon not found', { status: 404, headers: corsHeaders });
-}
-
-async function handleSites(request, env, corsHeaders) {
- if (request.method === 'GET') {
- const sitesData = (await env.NAV_KV.get('sites', { type: 'json' })) || [];
- const normalized = sitesData.map((s) => ({ ...s, clicks: typeof s.clicks === 'number' ? s.clicks : 0 }));
- return new Response(JSON.stringify(normalized), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- if (request.method === 'POST') {
- if (!verifyAuth(request, env)) {
- return new Response('Unauthorized', { status: 401, headers: corsHeaders });
- }
- const newSite = await request.json();
- const sites = (await env.NAV_KV.get('sites', { type: 'json' })) || [];
- const categories = (await env.NAV_KV.get('categories', { type: 'json' })) || [];
- newSite.id = Date.now().toString();
- newSite.clicks = 0;
- sites.push(newSite);
- if (newSite.category && !categories.includes(newSite.category)) {
- categories.push(newSite.category);
- await env.NAV_KV.put('categories', JSON.stringify(categories));
- }
- await env.NAV_KV.put('sites', JSON.stringify(sites));
- return new Response(JSON.stringify(newSite), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- return new Response('Method not allowed', { status: 405, headers: corsHeaders });
-}
-
-async function handleSite(request, env, id, corsHeaders) {
- const sites = (await env.NAV_KV.get('sites', { type: 'json' })) || [];
- const categories = (await env.NAV_KV.get('categories', { type: 'json' })) || [];
-
- if (request.method === 'GET') {
- const site = sites.find((s) => s.id === id);
- if (!site) return new Response('Not Found', { status: 404, headers: corsHeaders });
- const normalized = { ...site, clicks: typeof site.clicks === 'number' ? site.clicks : 0 };
- return new Response(JSON.stringify(normalized), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- if (request.method === 'PUT') {
- if (!verifyAuth(request, env)) {
- return new Response('Unauthorized', { status: 401, headers: corsHeaders });
- }
- const updatedSite = await request.json();
- const index = sites.findIndex((s) => s.id === id);
- if (index === -1) return new Response('Not Found', { status: 404, headers: corsHeaders });
- const existingClicks = typeof sites[index].clicks === 'number' ? sites[index].clicks : 0;
- sites[index] = { ...sites[index], ...updatedSite, id, clicks: existingClicks };
- if (updatedSite.category && !categories.includes(updatedSite.category)) {
- categories.push(updatedSite.category);
- await env.NAV_KV.put('categories', JSON.stringify(categories));
- }
- await env.NAV_KV.put('sites', JSON.stringify(sites));
- return new Response(JSON.stringify(sites[index]), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- if (request.method === 'DELETE') {
- if (!verifyAuth(request, env)) {
- return new Response('Unauthorized', { status: 401, headers: corsHeaders });
- }
- const index = sites.findIndex((s) => s.id === id);
- if (index === -1) return new Response('Not Found', { status: 404, headers: corsHeaders });
- sites.splice(index, 1);
- await env.NAV_KV.put('sites', JSON.stringify(sites));
- return new Response(JSON.stringify({ success: true }), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- return new Response('Method not allowed', { status: 405, headers: corsHeaders });
-}
-
-async function handleSiteClick(request, env, id, corsHeaders) {
- if (request.method !== 'POST') {
- return new Response('Method not allowed', { status: 405, headers: corsHeaders });
- }
- const sites = (await env.NAV_KV.get('sites', { type: 'json' })) || [];
- const index = sites.findIndex((s) => s.id === id);
- if (index === -1) {
- return new Response('Not Found', { status: 404, headers: corsHeaders });
- }
- const site = sites[index];
- const prev = typeof site.clicks === 'number' ? site.clicks : 0;
- sites[index] = { ...site, clicks: prev + 1 };
- await env.NAV_KV.put('sites', JSON.stringify(sites));
- return new Response(JSON.stringify({ success: true, clicks: prev + 1 }), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
-}
-
-async function handleCategories(request, env, corsHeaders) {
- if (request.method === 'GET') {
- let categories = (await env.NAV_KV.get('categories', { type: 'json' })) || [];
- if (!categories.length) {
- const sites = (await env.NAV_KV.get('sites', { type: 'json' })) || [];
- categories = [...new Set(sites.map((s) => s.category).filter(Boolean))];
- if (categories.length) await env.NAV_KV.put('categories', JSON.stringify(categories));
- }
- return new Response(JSON.stringify(categories), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- if (request.method === 'POST') {
- if (!verifyAuth(request, env)) {
- return new Response('Unauthorized', { status: 401, headers: corsHeaders });
- }
- const { name } = await request.json();
- if (!name || !name.trim()) {
- return new Response('Bad Request', { status: 400, headers: corsHeaders });
- }
- const categories = (await env.NAV_KV.get('categories', { type: 'json' })) || [];
- if (!categories.includes(name.trim())) {
- categories.push(name.trim());
- await env.NAV_KV.put('categories', JSON.stringify(categories));
- }
- return new Response(JSON.stringify({ success: true }), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- return new Response('Method not allowed', { status: 405, headers: corsHeaders });
-}
-
-async function handleCategory(request, env, name, corsHeaders) {
- if (!name) return new Response('Bad Request', { status: 400, headers: corsHeaders });
- if (!verifyAuth(request, env)) {
- return new Response('Unauthorized', { status: 401, headers: corsHeaders });
- }
- const categories = (await env.NAV_KV.get('categories', { type: 'json' })) || [];
-
- if (request.method === 'DELETE') {
- const filtered = categories.filter((c) => c !== name);
- await env.NAV_KV.put('categories', JSON.stringify(filtered));
- return new Response(JSON.stringify({ success: true }), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- if (request.method === 'PUT') {
- const body = await request.json();
- const newName = (body?.name || '').trim();
- if (!newName) return new Response('Bad Request', { status: 400, headers: corsHeaders });
- const updated = categories.map((c) => (c === name ? newName : c));
- const unique = Array.from(new Set(updated));
- await env.NAV_KV.put('categories', JSON.stringify(unique));
- const sites = (await env.NAV_KV.get('sites', { type: 'json' })) || [];
- const updatedSites = sites.map((site) =>
- site.category === name ? { ...site, category: newName } : site
- );
- await env.NAV_KV.put('sites', JSON.stringify(updatedSites));
- return new Response(JSON.stringify({ success: true }), {
- headers: { ...corsHeaders, 'Content-Type': 'application/json' },
- });
- }
- return new Response('Method not allowed', { status: 405, headers: corsHeaders });
-}
diff --git a/cf-nav-backend/wrangler.toml b/cf-nav-backend/wrangler.toml
deleted file mode 100644
index 7a12879..0000000
--- a/cf-nav-backend/wrangler.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-name = "cf-nav-backend"
-main = "worker.js"
-compatibility_date = "2026-01-01"
-
-# KV 命名空间(与原先 cf-nav 使用同一套数据可复用同一 id)
-[[kv_namespaces]]
-binding = "NAV_KV"
-id = "a89f429e1a684d2084eae8619755ee11"
-
-# 管理令牌/密码:与前端 config.js 的 ADMIN_TOKEN 一致;请求头带 Authorization: Bearer <本值> 才能写数据(也可用 wrangler secret put ADMIN_PASSWORD 设置)
-[vars]
-ADMIN_PASSWORD = "shumengya5201314"
-FAVICON_API = "https://cf-favicon.pages.dev/api/favicon?url="
-
-
diff --git a/cf-nav-frontend/README.md b/cf-nav-frontend/README.md
deleted file mode 100644
index 6eced1e..0000000
--- a/cf-nav-frontend/README.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# 萌芽导航 - 前后端分离版
-
-- **前端**:静态 PWA,部署到 **Cloudflare Pages**
-- **后端**:API Worker,部署到 **Cloudflare Workers**(目录 `cf-nav-backend`)
-
-## 一、后端(Cloudflare Worker)
-
-1. 进入后端目录并安装依赖、部署:
-
-```bash
-cd cf-nav-backend
-npm install
-npm run deploy
-```
-
-2. 修改 `cf-nav-backend/wrangler.toml`:
- - 如需新 KV:`wrangler kv:namespace create "NAV_KV"`,将返回的 `id` 填入 `[[kv_namespaces]]` 的 `id`
- - 修改 `[vars]` 中的 `ADMIN_PASSWORD`,或使用 `wrangler secret put ADMIN_PASSWORD` 设置密钥
-
-3. 部署成功后记下 Worker 地址,例如:
- `https://cf-nav-backend.你的子域.workers.dev`
-
-## 二、前端(Cloudflare Pages)
-
-1. **配置 API 地址**
- 编辑项目根目录下的 `config.js`,将 `window.API_BASE` 改为你的后端 Worker 地址,例如:
-
-```javascript
-window.API_BASE = 'https://cf-nav-backend.你的子域.workers.dev';
-```
-
-2. **部署到 Pages**
-
- - **方式 A - Git 推送**
- - 在 Cloudflare Dashboard → Pages → 创建项目 → 连接 Git
- - 构建:**无**(或留空)
- - 输出目录:`/`(根目录)
- - 部署后前端即可通过你的 `*.pages.dev` 或自定义域名访问
-
- - **方式 B - 直接上传**
- - Pages → 创建项目 → 直接上传
- - 将当前仓库根目录下所有前端文件(含 `config.js`、`index.html`、`app.js`、`admin.html`、`admin.js`、`styles.css`、`sw.js`、`manifest.webmanifest`、`logo.png`、`favicon.ico`、`offline.html`、`_redirects`)打包上传
-
-3. **本地预览**
-
-```bash
-npm install
-npm run dev
-```
-
-浏览器打开 `http://localhost:3000`。本地未配置同源 API 时,需在 `config.js` 中填写后端 Worker 地址才能正常请求数据。
-
-## 三、目录结构说明
-
-```
-mengya-nav/
-├── index.html # 首页
-├── admin.html # 后台
-├── app.js # 首页逻辑
-├── admin.js # 后台逻辑
-├── config.js # API 地址(部署前必改)
-├── styles.css
-├── sw.js # PWA Service Worker
-├── manifest.webmanifest
-├── offline.html
-├── logo.png / favicon.ico
-├── _redirects # 后台请直接访问 /admin.html(勿用 /admin 避免重定向)
-├── package.json # 前端脚本
-└── cf-nav-backend/ # 后端 Worker
- ├── worker.js # 仅 API,无静态资源
- ├── wrangler.toml
- └── package.json
-```
-
-## 四、CORS 与安全
-
-- 后端 Worker 已设置 `Access-Control-Allow-Origin: *`,Pages 域名可正常请求。
-- 生产环境建议在 Worker 中把 `Access-Control-Allow-Origin` 改为你的 Pages 域名,并务必修改/保管好 `ADMIN_PASSWORD`。
-
-## 五、数据迁移
-
-若之前已用旧版单 Worker 部署并使用了 KV,只需在 `cf-nav-backend/wrangler.toml` 中沿用原来的 KV namespace `id`,即可继续使用同一份数据,无需迁移。
diff --git a/cf-nav-frontend/admin.html b/cf-nav-frontend/admin.html
deleted file mode 100644
index 5ea7a82..0000000
--- a/cf-nav-frontend/admin.html
+++ /dev/null
@@ -1,417 +0,0 @@
-
-
-
-
-
-
-
-
-
- 萌芽导航-后台管理
-
-
-
-
-
-
-
-
-
-
-
-
-
-
⚠️ 无法进入后台
-
请使用带 token 的链接访问,例如:
-
/admin.html?token=管理员密钥
-
链接中的 token 需为后端 Worker 配置的管理员密钥(ADMIN_PASSWORD)。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | 名称 |
- URL |
- 分类 |
- 描述 |
- 标签 |
- 操作 |
-
-
-
-
-
-
-
-
-
-
-
-
-
- ✅
- 操作成功
-
-
-
-
-
-
-
diff --git a/cf-nav-frontend/admin.js b/cf-nav-frontend/admin.js
deleted file mode 100644
index 7cafceb..0000000
--- a/cf-nav-frontend/admin.js
+++ /dev/null
@@ -1,420 +0,0 @@
-// 后台管理 JavaScript(API 地址从 config.js 读取,token 仅从 URL ?token= 传入并由后端校验)
-const API_BASE = typeof window !== 'undefined' && window.API_BASE !== undefined ? window.API_BASE : '';
-
-// 从 URL 读取 token,不在前端校验,直接交给后端;无 token 则不显示后台
-const urlParams = new URLSearchParams(typeof location !== 'undefined' ? location.search : '');
-const authToken = urlParams.get('token') || null;
-
-let categories = [];
-let allSites = [];
-
-// 显示提示消息
-function showToast(message, type = 'success') {
- const toast = document.getElementById('toast');
- toast.className = `toast ${type} show`;
- document.querySelector('.toast-message').textContent = message;
-
- setTimeout(() => {
- toast.classList.remove('show');
- }, 3000);
-}
-
-// 显示无权限提示(无 token 或 token 错误)
-function showNoPermission() {
- const noPerm = document.getElementById('no-permission');
- const adminEl = document.getElementById('admin-container');
- if (noPerm) noPerm.style.display = 'block';
- if (adminEl) adminEl.style.display = 'none';
-}
-
-// 退出:跳转到当前页不带 query,下次进入需重新带 token
-document.getElementById('logout-btn').addEventListener('click', () => {
- if (typeof location !== 'undefined') location.href = location.pathname;
-});
-
-// 显示管理面板(仅 token 正确时)
-function showAdminPanel() {
- const noPerm = document.getElementById('no-permission');
- const adminEl = document.getElementById('admin-container');
- if (noPerm) noPerm.style.display = 'none';
- if (adminEl) adminEl.style.display = 'block';
- loadSites();
- loadCategories();
-}
-
-// 进入页时先向后端校验 token,通过才显示后台
-async function initAdmin() {
- if (!authToken) {
- showNoPermission();
- return;
- }
- try {
- const response = await fetch(`${API_BASE}/api/auth/check`, {
- headers: { 'Authorization': `Bearer ${authToken}` }
- });
- if (response.status !== 200) {
- showNoPermission();
- return;
- }
- const data = await response.json().catch(() => ({}));
- if (!data || !data.ok) {
- showNoPermission();
- return;
- }
- showAdminPanel();
- } catch (_) {
- showNoPermission();
- }
-}
-
-// 加载分类列表
-async function loadCategories() {
- try {
- const response = await fetch(`${API_BASE}/api/categories`, {
- headers: authToken ? { 'Authorization': `Bearer ${authToken}` } : {}
- });
- if (response.status === 401) {
- showNoPermission();
- return;
- }
- categories = response.ok ? await response.json() : [];
- renderCategoryList();
- updateCategorySelect();
- updateSiteCategoryFilterOptions();
- if (allSites.length) renderSites(allSites);
- } catch (error) {
- categories = [];
- renderCategoryList();
- }
-}
-
-// 渲染分类标签
-function renderCategoryList() {
- const container = document.getElementById('category-list');
- container.innerHTML = '';
-
- if (!categories.length) {
- const empty = document.createElement('div');
- empty.style.color = '#64748b';
- empty.textContent = '暂无分类';
- container.appendChild(empty);
- return;
- }
-
- categories.forEach(name => {
- const item = document.createElement('div');
- item.className = 'category-item';
- item.innerHTML = `
- ${name}
-
-
-
-
- `;
- container.appendChild(item);
- });
-}
-
-// 更新分类下拉
-function updateCategorySelect(selectedValue = '') {
- const select = document.getElementById('edit-site-category');
- if (!select) {
- return;
- }
-
- const options = ['默认'];
- const merged = Array.from(new Set([...options, ...categories].filter(Boolean)));
-
- select.innerHTML = '';
- merged.forEach(name => {
- const option = document.createElement('option');
- option.value = name;
- option.textContent = name;
- if (name === selectedValue) {
- option.selected = true;
- }
- select.appendChild(option);
- });
-}
-
-// 更新「网站列表」上方的分类筛选下拉
-function updateSiteCategoryFilterOptions() {
- const select = document.getElementById('site-category-filter');
- if (!select) return;
-
- const current = select.value;
- const fromSites = (allSites || []).map(s => s.category || '默认').filter(Boolean);
- const combined = Array.from(new Set(['默认', ...categories, ...fromSites]));
- select.innerHTML = '';
- combined.forEach(name => {
- const option = document.createElement('option');
- option.value = name;
- option.textContent = name;
- select.appendChild(option);
- });
- select.value = current || '';
-}
-
-// 根据当前筛选条件渲染网站表格
-function renderSites(sites) {
- const tbody = document.getElementById('sites-tbody');
- const filterEl = document.getElementById('site-category-filter');
- const categoryFilter = filterEl ? filterEl.value : '';
-
- const list = categoryFilter
- ? sites.filter(site => (site.category || '默认') === categoryFilter)
- : sites;
-
- tbody.innerHTML = '';
-
- list.forEach(site => {
- const tr = document.createElement('tr');
-
- const tagsHtml = site.tags && site.tags.length > 0
- ? `${site.tags.map(tag => `${tag}`).join('')}
`
- : '-';
-
- tr.innerHTML = `
- ${site.name} |
- ${site.url} |
- ${site.category} |
- ${site.description || '-'} |
- ${tagsHtml} |
-
-
-
-
-
- |
- `;
-
- tbody.appendChild(tr);
- });
-}
-
-// 加载网站列表
-async function loadSites() {
- try {
- const response = await fetch(`${API_BASE}/api/sites`, {
- headers: authToken ? { 'Authorization': `Bearer ${authToken}` } : {}
- });
- if (response.status === 401) {
- showNoPermission();
- return;
- }
- const sites = await response.json();
- allSites = sites || [];
-
- updateSiteCategoryFilterOptions();
- renderSites(allSites);
- } catch (error) {
- showToast('加载网站列表失败', 'error');
- }
-}
-
-// 网站列表按分类筛选
-const siteCategoryFilterEl = document.getElementById('site-category-filter');
-if (siteCategoryFilterEl) {
- siteCategoryFilterEl.addEventListener('change', () => {
- renderSites(allSites);
- });
-}
-
-// 添加新网站
-document.getElementById('add-new-site').addEventListener('click', () => {
- if (!authToken) return;
- document.getElementById('modal-title').textContent = '添加新网站';
- document.getElementById('edit-site-id').value = '';
- document.getElementById('edit-site-form').reset();
- updateCategorySelect();
- document.getElementById('edit-modal').classList.add('active');
-});
-
-// 编辑网站
-async function editSite(id) {
- try {
- const response = await fetch(`${API_BASE}/api/sites/${id}`);
- const site = await response.json();
-
- document.getElementById('modal-title').textContent = '编辑网站';
- document.getElementById('edit-site-id').value = site.id;
- document.getElementById('edit-site-name').value = site.name;
- document.getElementById('edit-site-url').value = site.url;
- document.getElementById('edit-site-description').value = site.description || '';
- updateCategorySelect(site.category);
- document.getElementById('edit-site-tags').value = site.tags ? site.tags.join(', ') : '';
-
- document.getElementById('edit-modal').classList.add('active');
- } catch (error) {
- showToast('加载网站信息失败', 'error');
- }
-}
-
-// 删除网站
-async function deleteSite(id) {
- if (!confirm('确定要删除这个网站吗?')) {
- return;
- }
-
- try {
- const response = await fetch(`${API_BASE}/api/sites/${id}`, {
- method: 'DELETE',
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (response.ok) {
- showToast('网站删除成功');
- loadSites();
- } else {
- showToast('删除失败', 'error');
- }
- } catch (error) {
- showToast('删除请求失败', 'error');
- }
-}
-
-// 保存网站(添加或更新)
-document.getElementById('edit-site-form').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const id = document.getElementById('edit-site-id').value;
- const site = {
- name: document.getElementById('edit-site-name').value.trim(),
- url: document.getElementById('edit-site-url').value.trim(),
- description: document.getElementById('edit-site-description').value.trim(),
- category: document.getElementById('edit-site-category').value,
- tags: document.getElementById('edit-site-tags').value
- .split(',')
- .map(tag => tag.trim())
- .filter(tag => tag)
- };
-
- // 验证URL
- if (!site.url.startsWith('http://') && !site.url.startsWith('https://')) {
- site.url = 'https://' + site.url;
- }
-
- try {
- const url = id ? `${API_BASE}/api/sites/${id}` : `${API_BASE}/api/sites`;
- const method = id ? 'PUT' : 'POST';
-
- const response = await fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify(site)
- });
-
- if (response.ok) {
- showToast(id ? '网站更新成功' : '网站添加成功');
- document.getElementById('edit-modal').classList.remove('active');
- loadSites();
- } else {
- showToast('保存失败', 'error');
- }
- } catch (error) {
- showToast('保存请求失败', 'error');
- }
-});
-
-// 关闭模态框
-document.getElementById('close-edit-modal').addEventListener('click', () => {
- document.getElementById('edit-modal').classList.remove('active');
-});
-
-document.getElementById('edit-modal').addEventListener('click', (e) => {
- if (e.target.id === 'edit-modal') {
- document.getElementById('edit-modal').classList.remove('active');
- }
-});
-
-// 添加分类
-document.getElementById('add-category-btn').addEventListener('click', async () => {
- const input = document.getElementById('new-category-name');
- const name = input.value.trim();
- if (!name) {
- return;
- }
-
- try {
- const response = await fetch(`${API_BASE}/api/categories`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify({ name })
- });
-
- if (response.ok) {
- input.value = '';
- await loadCategories();
- showToast('分类添加成功');
- } else {
- showToast('分类添加失败', 'error');
- }
- } catch (error) {
- showToast('分类添加失败', 'error');
- }
-});
-
-// 编辑分类
-async function editCategory(oldName) {
- const newName = prompt('请输入新的分类名称', oldName);
- if (!newName || newName.trim() === oldName) {
- return;
- }
-
- try {
- const response = await fetch(`${API_BASE}/api/categories/${encodeURIComponent(oldName)}`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify({ name: newName.trim() })
- });
-
- if (response.ok) {
- await loadCategories();
- await loadSites();
- showToast('分类更新成功');
- } else {
- showToast('分类更新失败', 'error');
- }
- } catch (error) {
- showToast('分类更新失败', 'error');
- }
-}
-
-// 删除分类
-async function deleteCategory(name) {
- if (!confirm(`确定删除分类「${name}」吗?`)) {
- return;
- }
-
- try {
- const response = await fetch(`${API_BASE}/api/categories/${encodeURIComponent(name)}`, {
- method: 'DELETE',
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (response.ok) {
- await loadCategories();
- showToast('分类删除成功');
- } else {
- showToast('分类删除失败', 'error');
- }
- } catch (error) {
- showToast('分类删除失败', 'error');
- }
-}
-
-// 初始化
-initAdmin();
diff --git a/cf-nav-frontend/app.js b/cf-nav-frontend/app.js
deleted file mode 100644
index db2dc1b..0000000
--- a/cf-nav-frontend/app.js
+++ /dev/null
@@ -1,498 +0,0 @@
-// API 配置(从 config.js 读取,部署 Pages 时请设置后端 Worker 地址)
-const API_BASE = typeof window !== 'undefined' && window.API_BASE !== undefined ? window.API_BASE : '';
-
-// 初始数据(示例)
-let sites = [
- {
- id: '1',
- name: '百度',
- url: 'https://www.baidu.com',
- description: '全球最大的中文搜索引擎',
- category: '常用',
- tags: ['搜索', '中文']
- },
- {
- id: '2',
- name: '知乎',
- url: 'https://www.zhihu.com',
- description: '中文互联网高质量的问答社区',
- category: '社交',
- tags: ['问答', '知识']
- },
- {
- id: '3',
- name: 'GitHub',
- url: 'https://github.com',
- description: '全球最大的代码托管平台',
- category: '工作',
- tags: ['代码', '开发']
- },
- {
- id: '4',
- name: 'Bilibili',
- url: 'https://www.bilibili.com',
- description: '中国年轻世代高度聚集的文化社区',
- category: '娱乐',
- tags: ['视频', '弹幕']
- },
- {
- id: '5',
- name: '淘宝',
- url: 'https://www.taobao.com',
- description: '亚洲最大的购物网站',
- category: '购物',
- tags: ['电商', '购物']
- }
-
-
-];
-
-let deferredInstallPrompt = null;
-let installBtn = null;
-let hasRefreshing = false;
-
-function isStandaloneMode() {
- return window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
-}
-
-function createInstallButton() {
- if (installBtn) {
- return installBtn;
- }
-
- installBtn = document.createElement('button');
- installBtn.className = 'install-btn';
- installBtn.type = 'button';
- installBtn.textContent = '📲 安装应用';
- installBtn.style.cssText = `
- position: fixed;
- bottom: 20px;
- right: 20px;
- background: linear-gradient(135deg, #10b981, #059669);
- color: #fff;
- border: none;
- padding: 12px 24px;
- border-radius: 50px;
- cursor: pointer;
- font-weight: 600;
- font-size: 0.95rem;
- box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
- display: none;
- z-index: 1000;
- transition: all 0.2s ease;
- `;
-
- installBtn.addEventListener('mouseenter', () => {
- installBtn.style.transform = 'translateY(-2px)';
- installBtn.style.boxShadow = '0 6px 20px rgba(16, 185, 129, 0.4)';
- });
-
- installBtn.addEventListener('mouseleave', () => {
- installBtn.style.transform = 'translateY(0)';
- installBtn.style.boxShadow = '0 4px 15px rgba(16, 185, 129, 0.3)';
- });
-
- installBtn.addEventListener('click', async () => {
- if (!deferredInstallPrompt) {
- return;
- }
-
- deferredInstallPrompt.prompt();
- const choice = await deferredInstallPrompt.userChoice;
-
- if (choice.outcome === 'accepted') {
- showToast('已触发安装流程');
- }
-
- deferredInstallPrompt = null;
- installBtn.style.display = 'none';
- });
-
- document.body.appendChild(installBtn);
- return installBtn;
-}
-
-function initInstallPrompt() {
- const button = createInstallButton();
-
- if (isStandaloneMode()) {
- button.style.display = 'none';
- return;
- }
-
- window.addEventListener('beforeinstallprompt', (event) => {
- event.preventDefault();
- deferredInstallPrompt = event;
- button.style.display = 'block';
- });
-
- window.addEventListener('appinstalled', () => {
- deferredInstallPrompt = null;
- button.style.display = 'none';
- showToast('应用安装成功');
- });
-}
-
-async function registerServiceWorker() {
- if (!('serviceWorker' in navigator)) {
- return;
- }
-
- try {
- const registration = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
-
- if (registration.waiting) {
- promptRefresh(registration.waiting);
- }
-
- registration.addEventListener('updatefound', () => {
- const newWorker = registration.installing;
- if (!newWorker) {
- return;
- }
-
- newWorker.addEventListener('statechange', () => {
- if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
- promptRefresh(newWorker);
- }
- });
- });
-
- navigator.serviceWorker.addEventListener('controllerchange', () => {
- if (hasRefreshing) {
- return;
- }
- hasRefreshing = true;
- window.location.reload();
- });
- } catch (error) {
- console.error('Service Worker 注册失败:', error);
- }
-}
-
-function promptRefresh(worker) {
- const shouldRefresh = window.confirm('发现新版本,是否立即刷新?');
- if (shouldRefresh) {
- worker.postMessage('SKIP_WAITING');
- }
-}
-
-
-
-// 加载网站数据
-async function loadSites() {
- try {
- const response = await fetch(`${API_BASE}/api/sites`);
- if (response.ok) {
- sites = await response.json();
- updateStats();
- renderSites();
- }
- } catch (error) {
- console.error('加载网站数据失败:', error);
- // 使用默认数据
- updateStats();
- renderSites();
- }
-}
-
-// 更新统计信息
-function updateStats() {
- const totalSites = sites.length;
- const categories = [...new Set(sites.map(site => site.category))];
- const allTags = sites.flatMap(site => site.tags || []);
- const uniqueTags = [...new Set(allTags)];
-
- document.getElementById('total-sites').textContent = totalSites;
- document.getElementById('total-categories').textContent = categories.length;
- document.getElementById('total-tags').textContent = uniqueTags.length;
-
- // 更新分类过滤器
- updateCategoryFilters();
-}
-
-// 更新分类过滤器
-function updateCategoryFilters() {
- const categoryFilters = document.getElementById('category-filters');
- const categories = ['all', ...new Set(sites.map(site => site.category))];
-
- categoryFilters.innerHTML = '';
-
- categories.forEach(category => {
- const filterBtn = document.createElement('div');
- filterBtn.className = 'category-filter';
- filterBtn.textContent = category === 'all' ? '全部' : category;
- filterBtn.dataset.category = category;
-
- if (category === 'all') {
- filterBtn.classList.add('active');
- }
-
- filterBtn.addEventListener('click', () => {
- document.querySelectorAll('.category-filter').forEach(btn => {
- btn.classList.remove('active');
- });
- filterBtn.classList.add('active');
- filterSites();
- closeCategorySidebar();
- });
-
- categoryFilters.appendChild(filterBtn);
- });
-}
-
-// 渲染网站
-function renderSites(filteredSites = null) {
- const container = document.getElementById('categories-container');
- const searchInput = document.getElementById('search-input').value.toLowerCase();
- const activeCategory = document.querySelector('.category-filter.active').dataset.category;
-
- // 如果没有传入过滤后的网站,则使用全部网站
- const sitesToRender = filteredSites || sites;
-
- // 如果有搜索关键词,进一步过滤
- let finalSites = sitesToRender;
- if (searchInput) {
- finalSites = sitesToRender.filter(site =>
- site.name.toLowerCase().includes(searchInput) ||
- (site.description && site.description.toLowerCase().includes(searchInput)) ||
- (site.tags && site.tags.some(tag => tag.toLowerCase().includes(searchInput)))
- );
- }
-
- // 如果有分类过滤
- if (activeCategory !== 'all') {
- finalSites = finalSites.filter(site => site.category === activeCategory);
- }
-
- // 按分类分组
- const sitesByCategory = {};
- finalSites.forEach(site => {
- if (!sitesByCategory[site.category]) {
- sitesByCategory[site.category] = [];
- }
- sitesByCategory[site.category].push(site);
- });
-
- // 如果没有网站匹配
- if (Object.keys(sitesByCategory).length === 0) {
- container.innerHTML = `
-
-
🔍
-
没有找到匹配的网站
-
尝试调整搜索关键词或分类筛选条件
-
- `;
- return;
- }
-
- // 渲染分类区块
- container.innerHTML = '';
- Object.keys(sitesByCategory).sort().forEach(category => {
- const categorySection = document.createElement('div');
- categorySection.className = 'category-section';
-
- categorySection.innerHTML = `
- ${category}
-
- ${sitesByCategory[category].map(site => createSiteCard(site)).join('')}
-
- `;
-
- container.appendChild(categorySection);
- });
-
- // 添加点击事件
- document.querySelectorAll('.site-card').forEach(card => {
- card.addEventListener('click', (e) => {
- if (!e.target.closest('.site-icon') && !e.target.closest('img')) {
- window.open(card.dataset.url, '_blank');
- }
- });
- });
-}
-
-// 通过 Worker 代理获取 favicon
-function getFaviconUrl(domain) {
- return `${API_BASE}/api/favicon?domain=${domain}`;
-}
-
-// 生成favicon HTML,使用 Worker 代理
-function generateFaviconHtml(domain, firstLetter) {
- const faviconUrl = getFaviconUrl(domain);
-
- // Worker 会自动尝试多个源,失败时显示占位符
- const onerrorCode = `this.parentElement.innerHTML='${firstLetter}
';`;
-
- return {
- src: faviconUrl,
- onerror: onerrorCode
- };
-}
-
-// 创建网站卡片HTML
-function createSiteCard(site) {
- // 从URL提取域名用于获取favicon
- const domain = new URL(site.url).hostname.replace('www.', '');
-
- // 生成网站名称首字母作为备用图标
- const firstLetter = site.name.charAt(0).toUpperCase();
-
- // 获取favicon配置
- const faviconConfig = generateFaviconHtml(domain, firstLetter);
-
- // 处理标签
- const tagsHtml = site.tags && site.tags.length > 0
- ? site.tags.map(tag => `${tag}`).join('')
- : '';
-
- // 处理标签文本(用于 title)
- const tagsText = site.tags && site.tags.length > 0
- ? site.tags.join('、')
- : '';
-
- const clickCount = typeof site.clicks === 'number' ? site.clicks : 0;
-
- return `
-
- ${clickCount}
-
-

-
-
-
${site.name}
-
${site.description || ''}
-
- ${tagsHtml}
-
- `;
-}
-
-// 显示提示消息
-function showToast(message, type = 'success') {
- const toast = document.getElementById('toast');
- toast.className = `toast ${type} show`;
- document.querySelector('.toast-message').textContent = message;
-
- setTimeout(() => {
- toast.classList.remove('show');
- }, 3000);
-}
-
-// 过滤网站(搜索和分类)
-function filterSites() {
- renderSites();
-}
-
-// 显示提示消息
-function showToast(message, type = 'success') {
- const toast = document.getElementById('toast');
- toast.className = `toast ${type} show`;
- document.querySelector('.toast-message').textContent = message;
-
- setTimeout(() => {
- toast.classList.remove('show');
- }, 3000);
-}
-
-// 过滤网站(搜索和分类)
-function filterSites() {
- renderSites();
-}
-
-// 网页搜索处理
-function performWebSearch(query, engine) {
- const encodedQuery = encodeURIComponent(query);
- let searchUrl = '';
-
- switch (engine) {
- case 'google':
- searchUrl = `https://www.google.com/search?q=${encodedQuery}`;
- break;
- case 'baidu':
- searchUrl = `https://www.baidu.com/s?wd=${encodedQuery}`;
- break;
- case 'bing':
- searchUrl = `https://www.bing.com/search?q=${encodedQuery}`;
- break;
- case 'duckduckgo':
- searchUrl = `https://duckduckgo.com/?q=${encodedQuery}`;
- break;
- case 'yandex':
- searchUrl = `https://yandex.com/search/?text=${encodedQuery}`;
- break;
- default:
- searchUrl = `https://www.google.com/search?q=${encodedQuery}`;
- }
-
- window.open(searchUrl, '_blank');
-}
-
-function openCategorySidebar() {
- document.body.classList.add('category-sidebar-open');
- const backdrop = document.getElementById('category-sidebar-backdrop');
- if (backdrop) backdrop.setAttribute('aria-hidden', 'false');
-}
-
-function closeCategorySidebar() {
- document.body.classList.remove('category-sidebar-open');
- const backdrop = document.getElementById('category-sidebar-backdrop');
- if (backdrop) backdrop.setAttribute('aria-hidden', 'true');
-}
-
-function initCategorySidebar() {
- const toggle = document.getElementById('category-sidebar-toggle');
- const closeBtn = document.getElementById('category-sidebar-close');
- const backdrop = document.getElementById('category-sidebar-backdrop');
- if (toggle) toggle.addEventListener('click', openCategorySidebar);
- if (closeBtn) closeBtn.addEventListener('click', closeCategorySidebar);
- if (backdrop) backdrop.addEventListener('click', closeCategorySidebar);
-}
-
-// 初始化
-document.addEventListener('DOMContentLoaded', async () => {
- initInstallPrompt();
- initCategorySidebar();
-
- // 点击卡片时上报访问次数(不阻止跳转)
- const container = document.getElementById('categories-container');
- if (container) {
- container.addEventListener('click', (e) => {
- const card = e.target.closest('.site-card');
- if (!card) return;
- const id = card.dataset.siteId;
- if (id) {
- const base = typeof window !== 'undefined' && window.API_BASE !== undefined ? window.API_BASE : '';
- fetch(base + '/api/sites/' + id + '/click', { method: 'POST', keepalive: true }).catch(() => {});
- }
- });
- }
-
- // 注册 PWA Service Worker
- await registerServiceWorker();
-
- // 加载网站数据
- await loadSites();
-
- // 搜索输入
- document.getElementById('search-input').addEventListener('input', filterSites);
-
- // 网页搜索表单提交
- const webSearchForm = document.getElementById('web-search-form');
- if (webSearchForm) {
- webSearchForm.addEventListener('submit', (e) => {
- e.preventDefault();
- const query = document.getElementById('web-search-input').value.trim();
- const engine = document.getElementById('search-engine').value;
-
- if (query) {
- performWebSearch(query, engine);
- document.getElementById('web-search-input').value = '';
- }
- });
- }
-});
diff --git a/cf-nav-frontend/apply-config.js b/cf-nav-frontend/apply-config.js
deleted file mode 100644
index 5d215d8..0000000
--- a/cf-nav-frontend/apply-config.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * 根据 config.js 应用站点名称到页面标题、meta、标题元素,并动态生成 PWA manifest
- * 需在 config.js 之后加载
- */
-(function () {
- var name = window.SITE_NAME || '萌芽导航';
- var shortName = window.SITE_SHORT_NAME || '萌芽';
- var desc = window.SITE_DESCRIPTION || (name + ' - 轻量好用的网址导航');
-
- document.title = name;
- var metaDesc = document.querySelector('meta[name="description"]');
- if (metaDesc) metaDesc.setAttribute('content', desc);
- var metaApp = document.querySelector('meta[name="apple-mobile-web-app-title"]');
- if (metaApp) metaApp.setAttribute('content', name);
-
- var siteTitle = document.getElementById('site-title');
- if (siteTitle) siteTitle.textContent = '\u2728 ' + name;
- var adminTitle = document.getElementById('admin-title');
- if (adminTitle) adminTitle.textContent = name + '-管理后台';
- var offlineTitle = document.getElementById('offline-title');
- if (offlineTitle) {
- document.title = name + ' - 离线';
- offlineTitle.textContent = name + ' - 离线';
- }
- var offlineLogo = document.getElementById('offline-logo');
- if (offlineLogo) offlineLogo.textContent = shortName.charAt(0);
-
- var glassOpacity = window.SITE_GLASS_OPACITY;
- if (typeof glassOpacity === 'number' && glassOpacity >= 0 && glassOpacity <= 1) {
- document.documentElement.style.setProperty('--site-glass-opacity', String(glassOpacity));
- }
-
- var isMobile = window.matchMedia('(max-width: 767px)').matches;
- var bgImages = isMobile
- ? window.SITE_MOBILE_BACKGROUND_IMAGES
- : window.SITE_DESKTOP_BACKGROUND_IMAGES;
- if (!Array.isArray(bgImages) || bgImages.length === 0) {
- bgImages = isMobile ? window.SITE_DESKTOP_BACKGROUND_IMAGES : window.SITE_MOBILE_BACKGROUND_IMAGES;
- }
- if (Array.isArray(bgImages) && bgImages.length > 0) {
- var imgUrl = bgImages[Math.floor(Math.random() * bgImages.length)];
- var applyBg = function () {
- if (!document.body) return;
- var bgEl = document.createElement('div');
- bgEl.className = 'site-bg';
- bgEl.setAttribute('aria-hidden', 'true');
- bgEl.style.backgroundImage = 'url(' + imgUrl + ')';
- document.body.insertBefore(bgEl, document.body.firstChild);
- };
- if (document.body) {
- applyBg();
- } else {
- document.addEventListener('DOMContentLoaded', applyBg);
- }
- }
-
- var manifest = {
- name: name,
- short_name: shortName,
- description: desc,
- lang: 'zh-CN',
- dir: 'ltr',
- id: '/',
- start_url: '/?source=pwa',
- scope: '/',
- display: 'standalone',
- display_override: ['standalone', 'minimal-ui', 'browser'],
- orientation: 'portrait-primary',
- theme_color: '#10b981',
- background_color: '#f8fafc',
- categories: ['productivity', 'utilities'],
- prefer_related_applications: false,
- icons: [
- { src: '/logo.png', type: 'image/png', sizes: '2048x2048', purpose: 'any' },
- { src: '/logo.png', type: 'image/png', sizes: '2048x2048', purpose: 'maskable' },
- { src: '/favicon.ico', type: 'image/x-icon', sizes: '16x16 24x24 32x32 48x48 64x64', purpose: 'any' }
- ]
- };
- var blob = new Blob([JSON.stringify(manifest)], { type: 'application/json' });
- var url = URL.createObjectURL(blob);
- var link = document.querySelector('link[rel="manifest"]');
- if (link) link.href = url;
- else {
- link = document.createElement('link');
- link.rel = 'manifest';
- link.href = url;
- document.head.appendChild(link);
- }
-})();
diff --git a/cf-nav-frontend/config.js b/cf-nav-frontend/config.js
deleted file mode 100644
index 6ea3ab9..0000000
--- a/cf-nav-frontend/config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * 前端配置(前后端分离)
- *
- * API 地址:
- * - 部署到 Cloudflare Pages 时改为你的 Worker 地址,如:
- * API_BASE = 'https://cf-nav-backend.你的子域.workers.dev';
- * - 本地同源或未配置时留空 ''
- *
- * 站点名称(可改为「XX导航」等,别人使用时改成自己的品牌):
- * - SITE_NAME: 完整名称,用于标题、PWA 名称等
- * - SITE_SHORT_NAME: 短名称,用于 PWA 桌面图标下方、离线页等
- * - SITE_DESCRIPTION: 站点描述,用于 meta description 和 PWA
- */
-window.API_BASE = 'https://cf-nav.api.smyhub.com';
-
-// 站点名称配置(可改成你自己的导航站名)
-window.SITE_NAME = '萌芽导航';
-window.SITE_SHORT_NAME = '萌芽';
-window.SITE_DESCRIPTION = '萌芽导航 - 轻量好用的网址导航';
-
-// 网站背景图(全屏随机一张,带约 30% 高斯模糊)。设为 [] 或不配置则使用默认渐变背景
-// 手机端用 SITE_MOBILE_BACKGROUND_IMAGES,电脑端用 SITE_DESKTOP_BACKGROUND_IMAGES(按视口宽度 768px 区分)
-window.SITE_MOBILE_BACKGROUND_IMAGES = [
- 'https://image.smyhub.com/file/手机壁纸/女生/1772108123232_VJ86r.jpg',
- 'https://image.smyhub.com/file/手机壁纸/女生/1772108022800_f945774e0a45f7a4afdc3da2b112025f.png',
- 'https://image.smyhub.com/file/手机壁纸/女生/1772108024006_3f9030ba77e355869115bc90fe019d53.png',
- "https://image.smyhub.com/file/手机壁纸/女生/1772108030393_159bbf61f88b38475ee9144a2e8e4956.png",
- "https://image.smyhub.com/file/手机壁纸/女生/1772108021977_8020902a0c8788538eee1cd06e784c6a.png",
- "https://image.smyhub.com/file/手机壁纸/女生/1772108021881_44374ab6c1daa54e0204bca48ac382f2.png",
-];
-
-window.SITE_DESKTOP_BACKGROUND_IMAGES = [
- 'https://image.smyhub.com/file/电脑壁纸/女生/cuSpSkq4.webp',
- 'https://image.smyhub.com/file/电脑壁纸/女生/5CrdoShv.webp',
- 'https://image.smyhub.com/file/电脑壁纸/女生/xTsVkCli.webp',
- "https://image.smyhub.com/file/电脑壁纸/女生/ItOJOHST.webp",
- "https://image.smyhub.com/file/电脑壁纸/女生/cUDkKiOf.webp",
- "https://image.smyhub.com/file/电脑壁纸/女生/c2HxMuGK.webp",
- "https://image.smyhub.com/file/电脑壁纸/女生/L0nQHehz.webp",
- "https://image.smyhub.com/file/电脑壁纸/女生/hj64Cqxn.webp",
-];
-
-// 内容区块(搜索框、分类块、网站卡片、顶栏)的半透明背景透明度,0=全透明 1=不透明,可自行修改
-window.SITE_GLASS_OPACITY = 0.25;
\ No newline at end of file
diff --git a/cf-nav-frontend/index.html b/cf-nav-frontend/index.html
deleted file mode 100644
index 257a567..0000000
--- a/cf-nav-frontend/index.html
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- 萌芽导航
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
📚 网站总数: 0
-
📁 分类数量: 0
-
🏷️ 标签数量: 0
-
-
- 🔍
-
-
-
-
-
-
-
-
📭
-
暂无网站
-
请在后台管理中添加网站
-
-
-
-
-
- ✅
- 操作成功
-
-
-
-
-
-
-
-
-
-
diff --git a/cf-nav-frontend/logo.png b/cf-nav-frontend/logo.png
deleted file mode 100644
index f719bf9..0000000
Binary files a/cf-nav-frontend/logo.png and /dev/null differ
diff --git a/cf-nav-frontend/offline.html b/cf-nav-frontend/offline.html
deleted file mode 100644
index f6ed4d8..0000000
--- a/cf-nav-frontend/offline.html
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
- 萌芽导航 - 离线
-
-
-
-
-
-
- 萌
- 当前离线,已切换离线页面
- 网络恢复后,刷新页面即可继续访问最新数据。已缓存的页面资源可以继续使用。
-
-
-
-
-
-
-
diff --git a/cf-nav-frontend/package-lock.json b/cf-nav-frontend/package-lock.json
deleted file mode 100644
index 6e8ebe6..0000000
--- a/cf-nav-frontend/package-lock.json
+++ /dev/null
@@ -1,1060 +0,0 @@
-{
- "name": "mengya-nav-frontend",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "mengya-nav-frontend",
- "version": "1.0.0",
- "devDependencies": {
- "serve": "^14.0.0"
- }
- },
- "node_modules/@zeit/schemas": {
- "version": "2.36.0",
- "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz",
- "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ansi-align": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
- "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.1.0"
- }
- },
- "node_modules/ansi-align/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-align/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/ansi-align/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-align/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/ansi-styles": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
- "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/arch": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
- "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/arg": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
- "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/boxen": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz",
- "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-align": "^3.0.1",
- "camelcase": "^7.0.0",
- "chalk": "^5.0.1",
- "cli-boxes": "^3.0.0",
- "string-width": "^5.1.2",
- "type-fest": "^2.13.0",
- "widest-line": "^4.0.1",
- "wrap-ansi": "^8.0.1"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/camelcase": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
- "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/chalk": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
- "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/chalk-template": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
- "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.2"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk-template?sponsor=1"
- }
- },
- "node_modules/chalk-template/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/chalk-template/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/cli-boxes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
- "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/clipboardy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
- "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "arch": "^2.2.0",
- "execa": "^5.1.1",
- "is-wsl": "^2.2.0"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/compressible": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": ">= 1.43.0 < 2"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/compression": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
- "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bytes": "3.1.2",
- "compressible": "~2.0.18",
- "debug": "2.6.9",
- "negotiator": "~0.6.4",
- "on-headers": "~1.1.0",
- "safe-buffer": "5.2.1",
- "vary": "~1.1.2"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/content-disposition": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10.17.0"
- }
- },
- "node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/is-docker": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "is-docker": "cli.js"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-port-reachable": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz",
- "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-docker": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/merge-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/mime-db": {
- "version": "1.54.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
- "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.18",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
- "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": "~1.33.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types/node_modules/mime-db": {
- "version": "1.33.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
- "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/negotiator": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
- "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/on-headers": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
- "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mimic-fn": "^2.1.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
- "dev": true,
- "license": "(WTFPL OR MIT)"
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/range-parser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
- "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dev": true,
- "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
- "dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "bin": {
- "rc": "cli.js"
- }
- },
- "node_modules/registry-auth-token": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
- "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "rc": "^1.1.6",
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/registry-url": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
- "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "rc": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/serve": {
- "version": "14.2.5",
- "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz",
- "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@zeit/schemas": "2.36.0",
- "ajv": "8.12.0",
- "arg": "5.0.2",
- "boxen": "7.0.0",
- "chalk": "5.0.1",
- "chalk-template": "0.4.0",
- "clipboardy": "3.0.0",
- "compression": "1.8.1",
- "is-port-reachable": "4.0.0",
- "serve-handler": "6.1.6",
- "update-check": "1.5.4"
- },
- "bin": {
- "serve": "build/main.js"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/serve-handler": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz",
- "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bytes": "3.0.0",
- "content-disposition": "0.5.2",
- "mime-types": "2.1.18",
- "minimatch": "3.1.2",
- "path-is-inside": "1.0.2",
- "path-to-regexp": "3.3.0",
- "range-parser": "1.2.0"
- }
- },
- "node_modules/serve-handler/node_modules/bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/serve-handler/node_modules/path-to-regexp": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz",
- "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
- "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/type-fest": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
- "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=12.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/update-check": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz",
- "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "registry-auth-token": "3.3.2",
- "registry-url": "3.1.0"
- }
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/widest-line": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
- "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "string-width": "^5.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- }
- }
-}
diff --git a/cf-nav-frontend/package.json b/cf-nav-frontend/package.json
deleted file mode 100644
index 46d72cf..0000000
--- a/cf-nav-frontend/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "mengya-nav-frontend",
- "version": "1.0.0",
- "description": "萌芽导航 - 前端 PWA(Cloudflare Pages)",
- "private": true,
- "scripts": {
- "build": "echo Static site - no build required",
- "dev": "npx serve . -l 3000",
- "preview": "npx serve . -l 3000"
- },
- "devDependencies": {
- "serve": "^14.0.0"
- }
-}
diff --git a/frontend/.env.desktop b/frontend/.env.desktop
new file mode 100644
index 0000000..cd98a02
--- /dev/null
+++ b/frontend/.env.desktop
@@ -0,0 +1,13 @@
+# 桌面壳加载本地 dist-desktop;API 指向线上 Worker
+VITE_APP_TARGET=desktop
+VITE_API_BASE=https://nav.smyhub.com
+
+VITE_FAVICON_API_BASE=https://favicon.smyhub.com/api/favicon?url=
+
+VITE_SITE_NAME=萌芽导航
+VITE_SITE_SHORT_NAME=萌芽导航
+VITE_SITE_DESCRIPTION=萌芽导航
+
+VITE_SITE_RANDOM_BG_API=https://randbg.api.smyhub.com
+VITE_SITE_BACKGROUND_BLUR=0px
+VITE_SITE_GLASS_OPACITY=0.92
diff --git a/frontend/.env.development b/frontend/.env.development
new file mode 100644
index 0000000..bfd1242
--- /dev/null
+++ b/frontend/.env.development
@@ -0,0 +1,16 @@
+# 同源部署 Workers 时保持默认空字符串即可
+VITE_APP_TARGET=web
+VITE_API_BASE=
+
+# 站点卡片 favicon 服务基址
+VITE_FAVICON_API_BASE=https://favicon.smyhub.com/api/favicon?url=
+
+VITE_SITE_NAME=萌芽导航
+VITE_SITE_SHORT_NAME=萌芽导航
+VITE_SITE_DESCRIPTION=萌芽导航
+
+# 随机背景 JSON API,留空则不加载背景图
+VITE_SITE_RANDOM_BG_API=https://randbg.api.smyhub.com
+VITE_SITE_BACKGROUND_BLUR=0px
+# 0–1,面板白底不透明度(越高越不透明、越少「玻璃感」)
+VITE_SITE_GLASS_OPACITY=0.92
diff --git a/frontend/.env.production b/frontend/.env.production
new file mode 100644
index 0000000..bfd1242
--- /dev/null
+++ b/frontend/.env.production
@@ -0,0 +1,16 @@
+# 同源部署 Workers 时保持默认空字符串即可
+VITE_APP_TARGET=web
+VITE_API_BASE=
+
+# 站点卡片 favicon 服务基址
+VITE_FAVICON_API_BASE=https://favicon.smyhub.com/api/favicon?url=
+
+VITE_SITE_NAME=萌芽导航
+VITE_SITE_SHORT_NAME=萌芽导航
+VITE_SITE_DESCRIPTION=萌芽导航
+
+# 随机背景 JSON API,留空则不加载背景图
+VITE_SITE_RANDOM_BG_API=https://randbg.api.smyhub.com
+VITE_SITE_BACKGROUND_BLUR=0px
+# 0–1,面板白底不透明度(越高越不透明、越少「玻璃感」)
+VITE_SITE_GLASS_OPACITY=0.92
diff --git a/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 0000000..c88b799
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-desktop
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 0000000..7dbf7eb
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,73 @@
+# React + TypeScript + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
+
+## React Compiler
+
+The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
+
+```js
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ // Other configs...
+
+ // Remove tseslint.configs.recommended and replace with this
+ tseslint.configs.recommendedTypeChecked,
+ // Alternatively, use this for stricter rules
+ tseslint.configs.strictTypeChecked,
+ // Optionally, add this for stylistic rules
+ tseslint.configs.stylisticTypeChecked,
+
+ // Other configs...
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+])
+```
+
+You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
+
+```js
+// eslint.config.js
+import reactX from 'eslint-plugin-react-x'
+import reactDom from 'eslint-plugin-react-dom'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ // Other configs...
+ // Enable lint rules for React
+ reactX.configs['recommended-typescript'],
+ // Enable lint rules for React DOM
+ reactDom.configs.recommended,
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+])
+```
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js
new file mode 100644
index 0000000..ef614d2
--- /dev/null
+++ b/frontend/eslint.config.js
@@ -0,0 +1,22 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..c6c80af
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 萌芽导航
+
+
+
+
+
+
+
+
+
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
new file mode 100644
index 0000000..0b9ab33
--- /dev/null
+++ b/frontend/package-lock.json
@@ -0,0 +1,2830 @@
+{
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5",
+ "react-router-dom": "^7.14.2"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^24.12.2",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.2.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.5.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.58.2",
+ "vite": "^8.0.10"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.3",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
+ "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@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.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@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.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@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.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.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.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+ "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
+ "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "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/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.23.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+ "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^3.0.5",
+ "debug": "^4.3.1",
+ "minimatch": "^10.2.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz",
+ "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+ "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "eslint": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+ "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz",
+ "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "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/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.127.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
+ "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
+ "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
+ "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
+ "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
+ "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
+ "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
+ "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.7",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
+ "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.12.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
+ "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz",
+ "integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/type-utils": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.59.1",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz",
+ "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz",
+ "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.59.1",
+ "@typescript-eslint/types": "^8.59.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz",
+ "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz",
+ "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz",
+ "integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz",
+ "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz",
+ "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.59.1",
+ "@typescript-eslint/tsconfig-utils": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz",
+ "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz",
+ "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.59.1",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
+ "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-rc.7"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.24",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz",
+ "integrity": "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "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.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001791",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz",
+ "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==",
+ "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/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "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/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.348",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.348.tgz",
+ "integrity": "sha512-QC2X59nRlycQQMc4ZXjSVBX+tSgJfgRtcrYHbIZLgOV2dCvefoQGegLR7lLXKgpPpSuVmJU19LMzGrSa2C7k3Q==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz",
+ "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.5",
+ "@eslint/config-helpers": "^0.5.5",
+ "@eslint/core": "^1.2.1",
+ "@eslint/plugin-kit": "^0.7.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^9.1.2",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "minimatch": "^10.2.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+ "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz",
+ "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": "^9 || ^10"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "17.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz",
+ "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "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/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.38",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz",
+ "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.13",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz",
+ "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==",
+ "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/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
+ "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz",
+ "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.5"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.14.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.2.tgz",
+ "integrity": "sha512-yCqNne6I8IB6rVCH7XUvlBK7/QKyqypBFGv+8dj4QBFJiiRX+FG7/nkdAvGElyvVZ/HQP5N19wzteuTARXi5Gw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.14.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.2.tgz",
+ "integrity": "sha512-YZcM5ES8jJSM+KrJ9BdvHHqlnGTg5tH3sC5ChFRj4inosKctdyzBDhOyyHdGk597q2OT6NTrCA1OvB/YDwfekQ==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.14.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
+ "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.127.0",
+ "@rolldown/pluginutils": "1.0.0-rc.17"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.17",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
+ }
+ },
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
+ "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.1.tgz",
+ "integrity": "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.59.1",
+ "@typescript-eslint/parser": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "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/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.0.10",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
+ "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.10",
+ "rolldown": "1.0.0-rc.17",
+ "tinyglobby": "^0.2.16"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "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/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.1.tgz",
+ "integrity": "sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..a238ddc
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build && vite build --mode desktop",
+ "build:web": "tsc -b && vite build",
+ "build:desktop": "tsc -b && vite build --mode desktop",
+ "dev:desktop": "vite --mode desktop",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5",
+ "react-router-dom": "^7.14.2"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^24.12.2",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.2.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.5.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.58.2",
+ "vite": "^8.0.10"
+ }
+}
diff --git a/cf-nav-frontend/favicon.ico b/frontend/public/favicon.ico
similarity index 100%
rename from cf-nav-frontend/favicon.ico
rename to frontend/public/favicon.ico
diff --git a/frontend/public/logo.png b/frontend/public/logo.png
new file mode 100644
index 0000000..fc463e1
Binary files /dev/null and b/frontend/public/logo.png differ
diff --git a/cf-nav-frontend/manifest.webmanifest b/frontend/public/manifest.webmanifest
similarity index 86%
rename from cf-nav-frontend/manifest.webmanifest
rename to frontend/public/manifest.webmanifest
index a3cd6e3..b1fe808 100644
--- a/cf-nav-frontend/manifest.webmanifest
+++ b/frontend/public/manifest.webmanifest
@@ -1,7 +1,7 @@
{
"name": "萌芽导航",
"short_name": "萌芽导航",
- "description": "萌芽导航 - 轻量好用的网址导航",
+ "description": "萌芽导航",
"lang": "zh-CN",
"dir": "ltr",
"id": "/",
@@ -10,8 +10,8 @@
"display": "standalone",
"display_override": ["standalone", "minimal-ui", "browser"],
"orientation": "portrait-primary",
- "theme_color": "#10b981",
- "background_color": "#f8fafc",
+ "theme_color": "#ea580c",
+ "background_color": "#fff7ed",
"categories": ["productivity", "utilities"],
"prefer_related_applications": false,
"icons": [
diff --git a/frontend/public/offline.html b/frontend/public/offline.html
new file mode 100644
index 0000000..e46bee7
--- /dev/null
+++ b/frontend/public/offline.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ 离线
+
+
+
+
+ 萌
+ 当前离线,已切换离线页面
+ 网络恢复后,刷新页面即可继续访问最新数据。已缓存的页面资源可以继续使用。
+
+
+
+
+
+
+
diff --git a/cf-nav-frontend/sw.js b/frontend/public/sw.js
similarity index 85%
rename from cf-nav-frontend/sw.js
rename to frontend/public/sw.js
index 9475042..f20f748 100644
--- a/cf-nav-frontend/sw.js
+++ b/frontend/public/sw.js
@@ -1,24 +1,17 @@
-const CACHE_VERSION = 'v2';
+/**
+ * PWA 缓存策略(与 React/Vite 产物配合;shell 仅预缓存关键静态,其余运行时 stale-while-revalidate)
+ */
+const CACHE_VERSION = 'v5';
const STATIC_CACHE = `mengya-static-${CACHE_VERSION}`;
const RUNTIME_CACHE = `mengya-runtime-${CACHE_VERSION}`;
const API_CACHE = `mengya-api-${CACHE_VERSION}`;
const OFFLINE_FALLBACK = '/offline.html';
-const APP_SHELL = [
- '/',
- '/index.html',
- '/styles.css',
- '/app.js',
- '/manifest.webmanifest',
- '/logo.png',
- '/favicon.ico',
- OFFLINE_FALLBACK,
-];
+/** Vite 构建后入口由服务器返回 index.html;此处预缓存根路径与离线 fallback */
+const APP_SHELL = ['/', '/offline.html', '/manifest.webmanifest', '/logo.png', '/favicon.ico'];
self.addEventListener('install', (event) => {
- event.waitUntil(
- caches.open(STATIC_CACHE).then((cache) => cache.addAll(APP_SHELL))
- );
+ event.waitUntil(caches.open(STATIC_CACHE).then((cache) => cache.addAll(APP_SHELL)));
self.skipWaiting();
});
@@ -161,7 +154,12 @@ async function cacheFirst(request, cacheName) {
}
function isStaticAssetRequest(request, url) {
- if (request.destination === 'script' || request.destination === 'style' || request.destination === 'image' || request.destination === 'font') {
+ if (
+ request.destination === 'script' ||
+ request.destination === 'style' ||
+ request.destination === 'image' ||
+ request.destination === 'font'
+ ) {
return true;
}
@@ -173,7 +171,8 @@ function isStaticAssetRequest(request, url) {
url.pathname.endsWith('.jpeg') ||
url.pathname.endsWith('.svg') ||
url.pathname.endsWith('.ico') ||
- url.pathname.endsWith('.webmanifest')
+ url.pathname.endsWith('.webmanifest') ||
+ url.pathname.endsWith('.woff2')
);
}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
new file mode 100644
index 0000000..bc27b5c
--- /dev/null
+++ b/frontend/src/App.tsx
@@ -0,0 +1,31 @@
+import { Suspense, lazy, useMemo } from 'react';
+import { BrowserRouter, HashRouter, Route, Routes, useLocation } from 'react-router-dom';
+import { IS_DESKTOP } from './config/site';
+import { useSiteBranding } from './hooks/useSiteBranding';
+
+const Home = lazy(() => import('./pages/Home'));
+const Admin = lazy(() => import('./pages/Admin'));
+
+function AppRoutes() {
+ const location = useLocation();
+ const isAdmin = useMemo(() => location.pathname.startsWith('/admin'), [location.pathname]);
+ useSiteBranding(isAdmin);
+
+ return (
+
+
+ } />
+ } />
+
+
+ );
+}
+
+export default function App() {
+ const Router = IS_DESKTOP ? HashRouter : BrowserRouter;
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts
new file mode 100644
index 0000000..c223d3f
--- /dev/null
+++ b/frontend/src/api/client.ts
@@ -0,0 +1,96 @@
+import { API_BASE } from '../config/site';
+import type { Site } from '../types/site';
+
+function url(path: string) {
+ return `${API_BASE}${path}`;
+}
+
+export async function fetchSites(init?: RequestInit): Promise {
+ const r = await fetch(url('/api/sites'), init);
+ if (!r.ok) throw new Error('加载网站失败');
+ return r.json();
+}
+
+export async function postSiteClick(siteId: string): Promise {
+ await fetch(url(`/api/sites/${siteId}/click`), { method: 'POST', keepalive: true }).catch(() => {});
+}
+
+export async function authLogin(password: string): Promise<{ ok: boolean; sessionId?: string }> {
+ const r = await fetch(url('/api/auth/login'), {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ password }),
+ });
+ const data = await r.json().catch(() => ({}));
+ if (!r.ok || !data?.ok || !data?.sessionId) return { ok: false };
+ return { ok: true, sessionId: data.sessionId as string };
+}
+
+export async function authCheck(token: string): Promise {
+ const r = await fetch(url('/api/auth/check'), {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (r.status !== 200) return false;
+ const data = await r.json().catch(() => ({}));
+ return Boolean(data?.ok);
+}
+
+export async function fetchCategories(token: string): Promise {
+ const r = await fetch(url('/api/categories'), {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (r.status === 401) throw new Error('unauthorized');
+ if (!r.ok) return [];
+ return r.json();
+}
+
+export async function fetchSiteById(id: string): Promise {
+ const r = await fetch(url(`/api/sites/${id}`));
+ if (!r.ok) throw new Error('加载站点失败');
+ return r.json();
+}
+
+export async function saveSite(token: string, site: Partial, id?: string): Promise {
+ const path = id ? `/api/sites/${id}` : '/api/sites';
+ const method = id ? 'PUT' : 'POST';
+ const r = await fetch(url(path), {
+ method,
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
+ body: JSON.stringify(site),
+ });
+ return r.ok;
+}
+
+export async function deleteSite(token: string, id: string): Promise {
+ const r = await fetch(url(`/api/sites/${id}`), {
+ method: 'DELETE',
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ return r.ok;
+}
+
+export async function addCategory(token: string, name: string): Promise {
+ const r = await fetch(url('/api/categories'), {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
+ body: JSON.stringify({ name }),
+ });
+ return r.ok;
+}
+
+export async function renameCategory(token: string, oldName: string, newName: string): Promise {
+ const r = await fetch(url(`/api/categories/${encodeURIComponent(oldName)}`), {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
+ body: JSON.stringify({ name: newName }),
+ });
+ return r.ok;
+}
+
+export async function deleteCategory(token: string, name: string): Promise {
+ const r = await fetch(url(`/api/categories/${encodeURIComponent(name)}`), {
+ method: 'DELETE',
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ return r.ok;
+}
diff --git a/frontend/src/components/AdminSiteCard.tsx b/frontend/src/components/AdminSiteCard.tsx
new file mode 100644
index 0000000..514e5f2
--- /dev/null
+++ b/frontend/src/components/AdminSiteCard.tsx
@@ -0,0 +1,59 @@
+import { useState } from 'react';
+import type { Site } from '../types/site';
+import { getFaviconUrl } from '../utils/favicon';
+
+interface AdminSiteCardProps {
+ site: Site;
+ onEdit: (id: string) => void;
+ onDelete: (id: string) => void;
+}
+
+/** 后台预览:与首页卡片相同的图标与排版,附带编辑 / 删除 / 打开 */
+export function AdminSiteCard({ site, onEdit, onDelete }: AdminSiteCardProps) {
+ const [imgFailed, setImgFailed] = useState(false);
+ const firstLetter = site.name.charAt(0).toUpperCase();
+ const faviconUrl = site.url ? getFaviconUrl(site.url) : '';
+ const tagsText = site.tags?.length ? site.tags.join('、') : '';
+ const clickCount = typeof site.clicks === 'number' ? site.clicks : 0;
+
+ return (
+
+
+ {clickCount}
+
+
+ {faviconUrl && !imgFailed ? (
+

setImgFailed(true)} />
+ ) : (
+
{firstLetter}
+ )}
+
+
+
+ {site.name}
+
+
+ {site.description || site.url}
+
+
+
+ {site.tags?.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+
+
+
+ 打开
+
+
+
+ );
+}
diff --git a/frontend/src/components/SiteCard.tsx b/frontend/src/components/SiteCard.tsx
new file mode 100644
index 0000000..58e08a1
--- /dev/null
+++ b/frontend/src/components/SiteCard.tsx
@@ -0,0 +1,55 @@
+import { useState } from 'react';
+import type { Site } from '../types/site';
+import { getFaviconUrl } from '../utils/favicon';
+
+interface SiteCardProps {
+ site: Site;
+}
+
+/** 导航卡片:外链、favicon、占位字母 */
+export function SiteCard({ site }: SiteCardProps) {
+ const [imgFailed, setImgFailed] = useState(false);
+
+ const firstLetter = site.name.charAt(0).toUpperCase();
+ const faviconUrl = site.url ? getFaviconUrl(site.url) : '';
+
+ const tagsText = site.tags?.length ? site.tags.join('、') : '';
+ const clickCount = typeof site.clicks === 'number' ? site.clicks : 0;
+
+ return (
+
+
+ {clickCount}
+
+
+ {faviconUrl && !imgFailed ? (
+

setImgFailed(true)} />
+ ) : (
+
{firstLetter}
+ )}
+
+
+
+ {site.name}
+
+
+ {site.description || ''}
+
+
+
+ {site.tags?.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+ );
+}
diff --git a/frontend/src/components/Toast.tsx b/frontend/src/components/Toast.tsx
new file mode 100644
index 0000000..2c6ca45
--- /dev/null
+++ b/frontend/src/components/Toast.tsx
@@ -0,0 +1,14 @@
+interface ToastProps {
+ message: string;
+ type: 'success' | 'error';
+ visible: boolean;
+}
+
+export function Toast({ message, type, visible }: ToastProps) {
+ return (
+
+ {type === 'success' ? '✅' : '⚠️'}
+ {message}
+
+ );
+}
diff --git a/frontend/src/config/site.ts b/frontend/src/config/site.ts
new file mode 100644
index 0000000..028e6c5
--- /dev/null
+++ b/frontend/src/config/site.ts
@@ -0,0 +1,22 @@
+/** 站点与 API 配置(可通过 .env 中以 VITE_ 前缀覆盖) */
+export const APP_TARGET = import.meta.env.VITE_APP_TARGET ?? 'web';
+export const IS_DESKTOP = APP_TARGET === 'desktop';
+
+export const API_BASE = import.meta.env.VITE_API_BASE ?? '';
+
+export const SITE_NAME = import.meta.env.VITE_SITE_NAME ?? '萌芽导航';
+export const SITE_SHORT_NAME = import.meta.env.VITE_SITE_SHORT_NAME ?? '萌芽导航';
+export const SITE_DESCRIPTION = import.meta.env.VITE_SITE_DESCRIPTION ?? '萌芽导航';
+
+/** 导航卡片站点图标:请求基址(须以 `url=` 结尾,后跟 encodeURIComponent(页面 http URL)) */
+export const SITE_FAVICON_API_BASE =
+ import.meta.env.VITE_FAVICON_API_BASE ?? 'https://favicon.smyhub.com/api/favicon?url=';
+
+/** 随机背景 JSON API,留空则不请求 */
+export const SITE_RANDOM_BG_API =
+ import.meta.env.VITE_SITE_RANDOM_BG_API ?? 'https://randbg.api.smyhub.com';
+
+export const SITE_BACKGROUND_BLUR = import.meta.env.VITE_SITE_BACKGROUND_BLUR ?? '0px';
+
+/** 面板白底不透明度 0–1(数值越高越「实」,越少玻璃感) */
+export const SITE_GLASS_OPACITY = Number(import.meta.env.VITE_SITE_GLASS_OPACITY ?? '0.92');
diff --git a/frontend/src/hooks/useInstallPrompt.ts b/frontend/src/hooks/useInstallPrompt.ts
new file mode 100644
index 0000000..708c544
--- /dev/null
+++ b/frontend/src/hooks/useInstallPrompt.ts
@@ -0,0 +1,55 @@
+import { useEffect, useRef, useState } from 'react';
+
+/** 最小类型(TS lib 可能未包含) */
+interface BeforeInstallPromptEvent extends Event {
+ prompt: () => Promise;
+ userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
+}
+
+function isStandaloneMode() {
+ return (
+ window.matchMedia('(display-mode: standalone)').matches ||
+ (window.navigator as Navigator & { standalone?: boolean }).standalone === true
+ );
+}
+
+/** PWA 安装按钮:beforeinstallprompt / appinstalled */
+export function useInstallPrompt(showToast: (msg: string, type?: 'success' | 'error') => void) {
+ const [visible, setVisible] = useState(false);
+ const deferredRef = useRef(null);
+
+ useEffect(() => {
+ if (isStandaloneMode()) return;
+
+ const onBeforeInstall = (e: Event) => {
+ e.preventDefault();
+ deferredRef.current = e as BeforeInstallPromptEvent;
+ setVisible(true);
+ };
+
+ const onInstalled = () => {
+ deferredRef.current = null;
+ setVisible(false);
+ showToast('应用安装成功');
+ };
+
+ window.addEventListener('beforeinstallprompt', onBeforeInstall);
+ window.addEventListener('appinstalled', onInstalled);
+ return () => {
+ window.removeEventListener('beforeinstallprompt', onBeforeInstall);
+ window.removeEventListener('appinstalled', onInstalled);
+ };
+ }, [showToast]);
+
+ async function triggerInstall() {
+ const ev = deferredRef.current;
+ if (!ev) return;
+ await ev.prompt();
+ const choice = await ev.userChoice;
+ if (choice.outcome === 'accepted') showToast('已触发安装流程');
+ deferredRef.current = null;
+ setVisible(false);
+ }
+
+ return { installVisible: visible, triggerInstall };
+}
diff --git a/frontend/src/hooks/useServiceWorker.ts b/frontend/src/hooks/useServiceWorker.ts
new file mode 100644
index 0000000..ae03a3e
--- /dev/null
+++ b/frontend/src/hooks/useServiceWorker.ts
@@ -0,0 +1,47 @@
+import { useEffect } from 'react';
+import { IS_DESKTOP } from '../config/site';
+
+/** 注册 SW:新版本 confirm + SKIP_WAITING;首次 controller 变化不强制刷新 */
+export function useServiceWorker() {
+ useEffect(() => {
+ if (IS_DESKTOP) return;
+ if (!('serviceWorker' in navigator)) return;
+
+ const pageAlreadyControlled = Boolean(navigator.serviceWorker.controller);
+ let hasRefreshing = false;
+
+ function promptRefresh(worker: ServiceWorker) {
+ const shouldRefresh = window.confirm('发现新版本,是否立即刷新?');
+ if (shouldRefresh) worker.postMessage('SKIP_WAITING');
+ }
+
+ (async () => {
+ try {
+ const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
+
+ if (reg.waiting) {
+ promptRefresh(reg.waiting);
+ }
+
+ reg.addEventListener('updatefound', () => {
+ const newWorker = reg.installing;
+ if (!newWorker) return;
+ newWorker.addEventListener('statechange', () => {
+ if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
+ promptRefresh(newWorker);
+ }
+ });
+ });
+
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
+ if (hasRefreshing) return;
+ if (!pageAlreadyControlled) return;
+ hasRefreshing = true;
+ window.location.reload();
+ });
+ } catch (e) {
+ console.error('Service Worker 注册失败:', e);
+ }
+ })();
+ }, []);
+}
diff --git a/frontend/src/hooks/useSiteBranding.ts b/frontend/src/hooks/useSiteBranding.ts
new file mode 100644
index 0000000..f521644
--- /dev/null
+++ b/frontend/src/hooks/useSiteBranding.ts
@@ -0,0 +1,109 @@
+import { useEffect } from 'react';
+import {
+ SITE_BACKGROUND_BLUR,
+ SITE_DESCRIPTION,
+ SITE_GLASS_OPACITY,
+ SITE_NAME,
+ SITE_RANDOM_BG_API,
+ SITE_SHORT_NAME,
+} from '../config/site';
+
+/** 随路由更新标题与 meta */
+export function useSiteBranding(isAdminRoute: boolean) {
+ useEffect(() => {
+ const pageTitle = isAdminRoute ? `${SITE_NAME}-管理后台` : SITE_NAME;
+ document.title = pageTitle;
+ const metaDesc = document.querySelector('meta[name="description"]');
+ if (metaDesc) metaDesc.setAttribute('content', SITE_DESCRIPTION);
+ const metaApp = document.querySelector('meta[name="apple-mobile-web-app-title"]');
+ if (metaApp) metaApp.setAttribute('content', SITE_NAME);
+ }, [isAdminRoute]);
+
+ useEffect(() => {
+ if (typeof SITE_GLASS_OPACITY === 'number' && SITE_GLASS_OPACITY >= 0 && SITE_GLASS_OPACITY <= 1) {
+ document.documentElement.style.setProperty('--site-glass-opacity', String(SITE_GLASS_OPACITY));
+ }
+ if (SITE_BACKGROUND_BLUR) {
+ document.documentElement.style.setProperty('--site-bg-blur', SITE_BACKGROUND_BLUR);
+ }
+
+ const manifest = {
+ name: SITE_NAME,
+ short_name: SITE_SHORT_NAME,
+ description: SITE_DESCRIPTION,
+ lang: 'zh-CN',
+ dir: 'ltr',
+ id: '/',
+ start_url: '/?source=pwa',
+ scope: '/',
+ display: 'standalone',
+ display_override: ['standalone', 'minimal-ui', 'browser'],
+ orientation: 'portrait-primary',
+ theme_color: '#ea580c',
+ background_color: '#fff7ed',
+ categories: ['productivity', 'utilities'],
+ prefer_related_applications: false,
+ icons: [
+ { src: '/logo.png', type: 'image/png', sizes: '2048x2048', purpose: 'any' },
+ { src: '/logo.png', type: 'image/png', sizes: '2048x2048', purpose: 'maskable' },
+ {
+ src: '/favicon.ico',
+ type: 'image/x-icon',
+ sizes: '16x16 24x24 32x32 48x48 64x64',
+ purpose: 'any',
+ },
+ ],
+ };
+ const blob = new Blob([JSON.stringify(manifest)], { type: 'application/json' });
+ const blobUrl = URL.createObjectURL(blob);
+ let link = document.querySelector('link[rel="manifest"]') as HTMLLinkElement | null;
+ if (!link) {
+ link = document.createElement('link');
+ link.rel = 'manifest';
+ document.head.appendChild(link);
+ }
+ link.href = blobUrl;
+
+ const apiBase = SITE_RANDOM_BG_API?.trim();
+
+ let cancelled = false;
+
+ function cleanupBg() {
+ document.querySelectorAll('.site-bg').forEach((el) => el.remove());
+ }
+
+ if (apiBase) {
+ const isMobile = window.matchMedia('(max-width: 767px)').matches;
+ const mode = isMobile ? 'mobile' : 'desktop';
+ const jsonUrl = `${apiBase.replace(/\/$/, '')}/api/random?format=json&mode=${encodeURIComponent(mode)}`;
+
+ fetch(jsonUrl)
+ .then((r) => {
+ if (!r.ok) throw new Error('bg api ' + r.status);
+ return r.json();
+ })
+ .then((data: { url?: string; src?: string }) => {
+ if (cancelled || !data) return;
+ const imgUrl = data.url || data.src;
+ if (!imgUrl || typeof imgUrl !== 'string') return;
+ const applyBg = () => {
+ cleanupBg();
+ const bgEl = document.createElement('div');
+ bgEl.className = 'site-bg';
+ bgEl.setAttribute('aria-hidden', 'true');
+ bgEl.style.backgroundImage = `url("${imgUrl.replace(/"/g, '\\"')}")`;
+ document.body.insertBefore(bgEl, document.body.firstChild);
+ };
+ if (document.body) applyBg();
+ else document.addEventListener('DOMContentLoaded', applyBg);
+ })
+ .catch(() => {});
+ }
+
+ return () => {
+ cancelled = true;
+ URL.revokeObjectURL(blobUrl);
+ cleanupBg();
+ };
+ }, []);
+}
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
new file mode 100644
index 0000000..317e4ef
--- /dev/null
+++ b/frontend/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import App from './App';
+import './styles/global.css';
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+);
diff --git a/frontend/src/pages/Admin.tsx b/frontend/src/pages/Admin.tsx
new file mode 100644
index 0000000..eb18ec3
--- /dev/null
+++ b/frontend/src/pages/Admin.tsx
@@ -0,0 +1,435 @@
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { useNavigate, useSearchParams } from 'react-router-dom';
+import {
+ addCategory,
+ authCheck,
+ deleteCategory,
+ deleteSite,
+ fetchCategories,
+ fetchSiteById,
+ fetchSites,
+ renameCategory,
+ saveSite,
+} from '../api/client';
+import { AdminSiteCard } from '../components/AdminSiteCard';
+import { Toast } from '../components/Toast';
+import { SITE_NAME } from '../config/site';
+import { useServiceWorker } from '../hooks/useServiceWorker';
+import type { Site } from '../types/site';
+import '../styles/admin.css';
+
+const ADMIN_SESSION_KEY = 'mengya_nav_admin_session';
+
+export default function Admin() {
+ useServiceWorker();
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+
+ const [authToken, setAuthToken] = useState(null);
+ const [checking, setChecking] = useState(true);
+
+ const [categories, setCategories] = useState([]);
+ const [allSites, setAllSites] = useState([]);
+ const [categoryFilter, setCategoryFilter] = useState('');
+ const [newCategoryName, setNewCategoryName] = useState('');
+
+ const [editOpen, setEditOpen] = useState(false);
+ const [editId, setEditId] = useState('');
+ const [editName, setEditName] = useState('');
+ const [editUrl, setEditUrl] = useState('');
+ const [editDescription, setEditDescription] = useState('');
+ const [editCategory, setEditCategory] = useState('');
+ const [editTags, setEditTags] = useState('');
+
+ const [toast, setToast] = useState<{ message: string; type: 'success' | 'error'; visible: boolean }>({
+ message: '',
+ type: 'success',
+ visible: false,
+ });
+
+ const showToast = useCallback((message: string, type: 'success' | 'error' = 'success') => {
+ setToast({ message, type, visible: true });
+ window.setTimeout(() => setToast((t) => ({ ...t, visible: false })), 3000);
+ }, []);
+
+ function redirectHome() {
+ try {
+ sessionStorage.removeItem(ADMIN_SESSION_KEY);
+ } catch (_) {}
+ navigate('/', { replace: true });
+ }
+
+ useEffect(() => {
+ let token = searchParams.get('token');
+ try {
+ if (!token) token = sessionStorage.getItem(ADMIN_SESSION_KEY);
+ } catch (_) {}
+
+ if (!token) {
+ redirectHome();
+ setChecking(false);
+ return;
+ }
+
+ let cancelled = false;
+ (async () => {
+ const ok = await authCheck(token!);
+ if (cancelled) return;
+ if (!ok) {
+ redirectHome();
+ setChecking(false);
+ return;
+ }
+ setAuthToken(token!);
+ setChecking(false);
+ })();
+
+ return () => {
+ cancelled = true;
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- 仅在挂载时校验入口 token
+ }, []);
+
+ const loadSites = useCallback(async () => {
+ if (!authToken) return;
+ try {
+ const sites = await fetchSites({ headers: { Authorization: `Bearer ${authToken}` } });
+ setAllSites(sites || []);
+ } catch {
+ showToast('加载网站列表失败', 'error');
+ }
+ }, [authToken, showToast]);
+
+ const loadCategoriesData = useCallback(async () => {
+ if (!authToken) return;
+ try {
+ const c = await fetchCategories(authToken);
+ setCategories(c);
+ } catch (e) {
+ if ((e as Error)?.message === 'unauthorized') redirectHome();
+ else setCategories([]);
+ }
+ }, [authToken]);
+
+ useEffect(() => {
+ if (!authToken) return;
+ void loadSites();
+ void loadCategoriesData();
+ }, [authToken, loadSites, loadCategoriesData]);
+
+ const mergedCategoryOptions = useMemo(() => {
+ const fromSites = (allSites || []).map((s) => s.category || '默认').filter(Boolean);
+ return Array.from(new Set(['默认', ...categories, ...fromSites]));
+ }, [categories, allSites]);
+
+ const filterSelectOptions = useMemo(() => {
+ return Array.from(new Set(['默认', ...categories, ...(allSites || []).map((s) => s.category || '默认')]));
+ }, [categories, allSites]);
+
+ const displayedSites = useMemo(() => {
+ if (!categoryFilter) return allSites;
+ return allSites.filter((s) => (s.category || '默认') === categoryFilter);
+ }, [allSites, categoryFilter]);
+
+ const sitesByCategory = useMemo(() => {
+ const map: Record = {};
+ for (const site of displayedSites) {
+ const c = site.category || '未分类';
+ if (!map[c]) map[c] = [];
+ map[c].push(site);
+ }
+ return map;
+ }, [displayedSites]);
+
+ const sortedCategoryKeys = useMemo(() => Object.keys(sitesByCategory).sort(), [sitesByCategory]);
+
+ function openAddModal() {
+ if (!authToken) return;
+ setEditId('');
+ setEditName('');
+ setEditUrl('');
+ setEditDescription('');
+ setEditTags('');
+ setEditCategory('');
+ setEditOpen(true);
+ }
+
+ function openAddModalForCategory(sectionTitle: string) {
+ if (!authToken) return;
+ setEditId('');
+ setEditName('');
+ setEditUrl('');
+ setEditDescription('');
+ setEditTags('');
+ setEditCategory(sectionTitle === '未分类' ? '' : sectionTitle);
+ setEditOpen(true);
+ }
+
+ async function openEditModal(id: string) {
+ if (!authToken) return;
+ try {
+ const site = await fetchSiteById(id);
+ setEditId(site.id);
+ setEditName(site.name);
+ setEditUrl(site.url);
+ setEditDescription(site.description || '');
+ setEditCategory(site.category || '');
+ setEditTags(site.tags?.join(', ') || '');
+ setEditOpen(true);
+ } catch {
+ showToast('加载网站信息失败', 'error');
+ }
+ }
+
+ async function onDeleteSite(id: string) {
+ if (!authToken || !confirm('确定要删除这个网站吗?')) return;
+ const ok = await deleteSite(authToken, id);
+ if (ok) {
+ showToast('网站删除成功');
+ await loadSites();
+ } else showToast('删除失败', 'error');
+ }
+
+ async function submitSite(e: React.FormEvent) {
+ e.preventDefault();
+ if (!authToken) return;
+ let url = editUrl.trim();
+ if (!url.startsWith('http://') && !url.startsWith('https://')) url = 'https://' + url;
+
+ const site: Partial = {
+ name: editName.trim(),
+ url,
+ description: editDescription.trim(),
+ category: editCategory,
+ tags: editTags
+ .split(',')
+ .map((x) => x.trim())
+ .filter(Boolean),
+ };
+
+ const ok = await saveSite(authToken, site, editId || undefined);
+ if (ok) {
+ showToast(editId ? '网站更新成功' : '网站添加成功');
+ setEditOpen(false);
+ await loadSites();
+ await loadCategoriesData();
+ } else showToast('保存失败', 'error');
+ }
+
+ async function onAddCategory() {
+ const name = newCategoryName.trim();
+ if (!authToken || !name) return;
+ const ok = await addCategory(authToken, name);
+ if (ok) {
+ setNewCategoryName('');
+ await loadCategoriesData();
+ showToast('分类添加成功');
+ } else showToast('分类添加失败', 'error');
+ }
+
+ async function onRenameCategory(oldName: string) {
+ if (!authToken) return;
+ const next = prompt('请输入新的分类名称', oldName);
+ if (!next || next.trim() === oldName) return;
+ const ok = await renameCategory(authToken, oldName, next.trim());
+ if (ok) {
+ await loadCategoriesData();
+ await loadSites();
+ showToast('分类更新成功');
+ } else showToast('分类更新失败', 'error');
+ }
+
+ async function onDeleteCategory(name: string) {
+ if (!authToken || !confirm(`确定删除分类「${name}」吗?`)) return;
+ const ok = await deleteCategory(authToken, name);
+ if (ok) {
+ await loadCategoriesData();
+ showToast('分类删除成功');
+ } else showToast('分类删除失败', 'error');
+ }
+
+ if (checking || !authToken) {
+ return null;
+ }
+
+ return (
+
+
+
+
{SITE_NAME}-管理后台
+
+
+
+
+
+
📁 分类管理
+
+ setNewCategoryName(e.target.value)}
+ />
+
+
+
+
+ {!categories.length ? (
+
暂无分类
+ ) : (
+ categories.map((name) => (
+
+ {name}
+
+
+
+
+
+ ))
+ )}
+
+
+
+
+
+
+
+
+
+
网站预览
+
+
+
+
+
+ {displayedSites.length === 0 ? (
+
+
📭
+
{categoryFilter ? '该分类下暂无网站' : '暂无网站,点击上方添加'}
+
+ ) : (
+
+ {sortedCategoryKeys.map((categoryName) => (
+
+
+ {categoryName}
+
+
+
+ {sitesByCategory[categoryName].map((site) => (
+
void openEditModal(id)}
+ onDelete={(id) => void onDeleteSite(id)}
+ />
+ ))}
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
{
+ if ((e.target as HTMLElement).id === 'edit-modal') setEditOpen(false);
+ }}
+ >
+
+
+
{editId ? '编辑网站' : '添加网站'}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
new file mode 100644
index 0000000..a7d97ad
--- /dev/null
+++ b/frontend/src/pages/Home.tsx
@@ -0,0 +1,361 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import type { CSSProperties } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { fetchSites, postSiteClick, authLogin } from '../api/client';
+import { SiteCard } from '../components/SiteCard';
+import { Toast } from '../components/Toast';
+import { SITE_NAME } from '../config/site';
+import { useInstallPrompt } from '../hooks/useInstallPrompt';
+import { useServiceWorker } from '../hooks/useServiceWorker';
+import type { Site } from '../types/site';
+
+const ADMIN_SESSION_STORAGE_KEY = 'mengya_nav_admin_session';
+const ADMIN_UNLOCK_CLICKS = 5;
+const ADMIN_UNLOCK_RESET_MS = 2800;
+
+function performWebSearch(query: string, engine: string) {
+ const encodedQuery = encodeURIComponent(query);
+ const urls: Record = {
+ google: `https://www.google.com/search?q=${encodedQuery}`,
+ baidu: `https://www.baidu.com/s?wd=${encodedQuery}`,
+ bing: `https://www.bing.com/search?q=${encodedQuery}`,
+ duckduckgo: `https://duckduckgo.com/?q=${encodedQuery}`,
+ yandex: `https://yandex.com/search/?text=${encodedQuery}`,
+ };
+ window.open(urls[engine] || urls.google, '_blank');
+}
+
+/** body 上切换 class,复用原侧边栏 CSS */
+function BodyClassWhen({ open, className }: { open: boolean; className: string }) {
+ useEffect(() => {
+ if (open) document.body.classList.add(className);
+ else document.body.classList.remove(className);
+ return () => document.body.classList.remove(className);
+ }, [open, className]);
+ return null;
+}
+
+const installBtnStyle: CSSProperties = {
+ position: 'fixed',
+ bottom: 20,
+ right: 20,
+ background: 'linear-gradient(145deg, #ea580c, #c2410c)',
+ color: '#fff',
+ border: 'none',
+ padding: '12px 24px',
+ borderRadius: '10px',
+ cursor: 'pointer',
+ fontWeight: 600,
+ fontSize: '0.95rem',
+ boxShadow: '0 4px 14px rgba(234, 88, 12, 0.28)',
+ zIndex: 1000,
+};
+
+export default function Home() {
+ useServiceWorker();
+ const navigate = useNavigate();
+
+ const [sites, setSites] = useState([]);
+ const [searchInput, setSearchInput] = useState('');
+ const [activeCategory, setActiveCategory] = useState('all');
+ const [sidebarOpen, setSidebarOpen] = useState(false);
+
+ const [toast, setToast] = useState<{ message: string; type: 'success' | 'error'; visible: boolean }>({
+ message: '',
+ type: 'success',
+ visible: false,
+ });
+
+ const showToast = useCallback((message: string, type: 'success' | 'error' = 'success') => {
+ setToast({ message, type, visible: true });
+ window.setTimeout(() => setToast((t) => ({ ...t, visible: false })), 3000);
+ }, []);
+
+ const { installVisible, triggerInstall } = useInstallPrompt(showToast);
+
+ const [adminModalOpen, setAdminModalOpen] = useState(false);
+ const [adminPassword, setAdminPassword] = useState('');
+ const [adminUnlockError, setAdminUnlockError] = useState('');
+ const logoClickRef = useRef(0);
+ const logoResetTimerRef = useRef(null);
+
+ useEffect(() => {
+ let cancelled = false;
+ (async () => {
+ try {
+ const data = await fetchSites();
+ if (!cancelled) setSites(data);
+ } catch {
+ if (!cancelled) showToast('加载网站数据失败', 'error');
+ }
+ })();
+ return () => {
+ cancelled = true;
+ };
+ }, [showToast]);
+
+ const categoriesList = useMemo(
+ () => ['all', ...new Set(sites.map((s) => s.category).filter(Boolean))] as string[],
+ [sites]
+ );
+
+ const stats = useMemo(() => {
+ const totalSites = sites.length;
+ const cats = [...new Set(sites.map((s) => s.category).filter(Boolean))];
+ const allTags = sites.flatMap((s) => s.tags || []);
+ const uniqueTags = [...new Set(allTags)];
+ return { totalSites, categoriesCount: cats.length, tagsCount: uniqueTags.length };
+ }, [sites]);
+
+ const filteredGrouped = useMemo(() => {
+ let list = sites;
+ if (searchInput.trim()) {
+ const q = searchInput.toLowerCase();
+ list = list.filter(
+ (site) =>
+ site.name.toLowerCase().includes(q) ||
+ (site.description && site.description.toLowerCase().includes(q)) ||
+ (site.tags && site.tags.some((tag) => tag.toLowerCase().includes(q)))
+ );
+ }
+ if (activeCategory !== 'all') {
+ list = list.filter((s) => s.category === activeCategory);
+ }
+ const map: Record = {};
+ list.forEach((site) => {
+ const c = site.category || '未分类';
+ if (!map[c]) map[c] = [];
+ map[c].push(site);
+ });
+ return map;
+ }, [sites, searchInput, activeCategory]);
+
+ function onLogoClick(e: React.MouseEvent) {
+ e.preventDefault();
+ logoClickRef.current += 1;
+ if (logoResetTimerRef.current) window.clearTimeout(logoResetTimerRef.current);
+ logoResetTimerRef.current = window.setTimeout(() => {
+ logoClickRef.current = 0;
+ logoResetTimerRef.current = null;
+ }, ADMIN_UNLOCK_RESET_MS);
+ if (logoClickRef.current >= ADMIN_UNLOCK_CLICKS) {
+ logoClickRef.current = 0;
+ if (logoResetTimerRef.current) window.clearTimeout(logoResetTimerRef.current);
+ setAdminPassword('');
+ setAdminUnlockError('');
+ setAdminModalOpen(true);
+ }
+ }
+
+ async function submitAdminUnlock() {
+ setAdminUnlockError('');
+ try {
+ const data = await authLogin(adminPassword);
+ if (!data.ok || !data.sessionId) {
+ setAdminUnlockError('密码错误或无法登录');
+ return;
+ }
+ try {
+ sessionStorage.setItem(ADMIN_SESSION_STORAGE_KEY, data.sessionId);
+ } catch (_) {}
+ setAdminModalOpen(false);
+ navigate('/admin');
+ } catch {
+ setAdminUnlockError('网络错误,请稍后重试');
+ }
+ }
+
+ function onContainerClick(e: React.MouseEvent) {
+ const card = (e.target as HTMLElement).closest('.site-card') as HTMLElement | null;
+ if (!card) return;
+ const id = card.dataset.siteId;
+ if (id) void postSiteClick(id);
+ }
+
+ return (
+ <>
+
+
+
+
+
+ {SITE_NAME}
+
+
+
+
+
+
+
+
+
+ 📚 网站总数: {stats.totalSites}
+
+
+ 📁 分类数量: {stats.categoriesCount}
+
+
+ 🏷️ 标签数量: {stats.tagsCount}
+
+
+
+ 🔍
+ setSearchInput(e.target.value)}
+ />
+
+
+
+
+ {sites.length === 0 ? (
+
+
📭
+
暂无网站
+
请在后台管理中添加网站
+
+ ) : Object.keys(filteredGrouped).length === 0 ? (
+
+
🔍
+
没有找到匹配的网站
+
尝试调整搜索关键词或分类筛选条件
+
+ ) : (
+ Object.keys(filteredGrouped)
+ .sort()
+ .map((category) => (
+
+
{category}
+
+ {filteredGrouped[category].map((site) => (
+
+ ))}
+
+
+ ))
+ )}
+
+
+
+
+
+
+