commit c226cf705401aae9481a7a0cc485d308a6c9f92c Author: ~ <1617530133@qq.com> Date: Sat Nov 7 10:43:06 2026 +0800 init: laimiao-web-new diff --git a/.env b/.env new file mode 100644 index 0000000..19b6677 --- /dev/null +++ b/.env @@ -0,0 +1,17 @@ +# --------------------------------------------------------------- +# 接口统一指向本地 team-api(localhost:8000) +# 本地: http://localhost:8000 +# 生产: https://laimiaoai.com +# /api 在 vite dev/preview 下由 vite.config.ts 代理到该地址; +# 登录页(login/)直接按绝对地址请求,依赖 team-api 的 CORS 配置。 +# --------------------------------------------------------------- +VITE_TEAM_API_URL=http://localhost:8000 + +# 微信公众号(开放平台)扫码登录 +VITE_WECHAT_APPID=wx30464a698b929bf4 + +# 技能 Agent 运行后端(laimiao-agent-v2) +VITE_SKILL_AGENT_URL=http://localhost:8100 + +# 技能 Agent 运行后端(会话列表 / 生成,laimiao-agent-v2) +VITE_AGENT_API_URL=http://localhost:8100 diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..e3fbf7f --- /dev/null +++ b/.env.production @@ -0,0 +1,5 @@ +# 生产环境(site.laimiaoai.com):同域访问 team-api 与 agent +VITE_TEAM_API_URL=/ +VITE_SKILL_AGENT_URL=/skill-agent +VITE_AGENT_API_URL=/skill-agent +VITE_WECHAT_APPID=wx30464a698b929bf4 diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..72b8948 --- /dev/null +++ b/.env.test @@ -0,0 +1,11 @@ +# 测试环境:https://laimiao-web-test.kapokcloud.com +# team-api / agent 均由测试服务器同源反向代理,避免跨域 +VITE_TEAM_API_URL=https://laimiao-web-test.kapokcloud.com +VITE_SKILL_AGENT_URL=/skill-agent +VITE_AGENT_API_URL=/skill-agent +VITE_WECHAT_APPID=wx30464a698b929bf4 +VITE_WECHAT_LOGIN_ORIGIN=https://laimiao-web-test.kapokcloud.com +# WeChat 授权回调域仅登记了 laimiaoai.com,先回调到该域名再由 Caddy 302 回测试域名 +VITE_WECHAT_CALLBACK_URL=https://laimiaoai.com/laimiao-web-test-callback +VITE_LAIMIAO_LOGIN_URL=/login/ +VITE_IMAGE_PROXY=false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/app.html b/app.html new file mode 100644 index 0000000..4beb171 --- /dev/null +++ b/app.html @@ -0,0 +1,103 @@ + + + + + + + + + + Mindora — Smarter Mental Health, Powered by AI + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + diff --git a/generate.html b/generate.html new file mode 100644 index 0000000..0deae21 --- /dev/null +++ b/generate.html @@ -0,0 +1,5184 @@ + + + + + + +开始创作|来秒AI + + + + + + + + + + + + +
+ +

就现在,把行业灵感变成作品

选择行业与技能,输入需求或上传产品图,来秒AI 一键成片

+
+ + +
+ +
+ + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..1c5a45b --- /dev/null +++ b/index.html @@ -0,0 +1,1372 @@ + + + + + + + 来秒AI|让每个行业的灵感,都能一秒成片 + + + + + + + + + + + +
+
+ + + + +
+ +

+ 让每个行业的灵感, + 都能一秒成片 +

+ +

从电商零售,到旅游海报,来秒AI 把创意快速变成可发布的图片。

+ +

好内容
被看见

+ + + +
+ +
+ + + +
+

零成本

+ + + + +
+
+ + + + diff --git a/industries b/industries new file mode 120000 index 0000000..813b871 --- /dev/null +++ b/industries @@ -0,0 +1 @@ +public/industries \ No newline at end of file diff --git a/invite/index.html b/invite/index.html new file mode 100644 index 0000000..1baf200 --- /dev/null +++ b/invite/index.html @@ -0,0 +1,459 @@ + + + + + + + + + 邀请好友|来秒AI + + + + + + + + +
+ + + 来秒AI + + +
+
+ + + + +
+
+ +
+ + + + diff --git a/library.html b/library.html new file mode 100644 index 0000000..796001d --- /dev/null +++ b/library.html @@ -0,0 +1,31 @@ + + + + + + + + + + 资产库|来秒AI + + + + + + + + +
+ + + diff --git a/login/index.html b/login/index.html new file mode 100644 index 0000000..caff846 --- /dev/null +++ b/login/index.html @@ -0,0 +1,79 @@ + + + + + + + + 微信扫码登录|来秒AI + + + + + + + + + +
+

微信扫码登录

+
+
+ + + + diff --git a/login/main.ts b/login/main.ts new file mode 100644 index 0000000..00bc4b1 --- /dev/null +++ b/login/main.ts @@ -0,0 +1,142 @@ +declare global { + interface Window { + WxLogin?: (options: Record) => void + } +} + +const TEAM_API = (import.meta.env.VITE_TEAM_API_URL || 'http://localhost:8000').replace(/\/+$/, '') +const WECHAT_APPID = import.meta.env.VITE_WECHAT_APPID || '' +const WECHAT_ORIGIN = (import.meta.env.VITE_WECHAT_LOGIN_ORIGIN || window.location.origin).replace(/\/+$/, '') +// 微信授权回调地址:默认落在当前域名的登录页;测试环境可用 VITE_WECHAT_CALLBACK_URL +// 指向已登记授权回调域的域名(如 laimiaoai.com)再中转回测试域名。 +const WECHAT_CALLBACK_URL = import.meta.env.VITE_WECHAT_CALLBACK_URL || `${WECHAT_ORIGIN}/login/` + +// 隐藏微信 iframe 自带的标题栏/状态栏,只保留二维码(与 tripcn.ai-web 登录页一致) +const LOGIN_QR_CSS = + 'data:text/css;base64,I3FyQ29kZUlmcmFtZSBpZnJhbWUgewogIHdpZHRoOiAxMDAlOwogIGhlaWdodDogMTAwJTsKICBvdmVyZmxvdzogaGlkZGVuOwp9Ci5pbXBvd2VyQm94IHsKICBkaXNwbGF5OiBmbGV4OwogIGp1c3RpZnktY29udGVudDogY2VudGVyOwogIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgd2lkdGg6IDEwMCU7CiAgaGVpZ2h0OiAxMDAlOwp9Ci5pbXBvd2VyQm94IC5xcmNvZGUgewogIHdpZHRoOiAyMDBweDsKICBoZWlnaHQ6IDIwMHB4OwogIHBhZGRpbmc6IDBweDsKICBtYXJnaW46IGF1dG87CiAgYm9yZGVyOiBub25lOwp9Ci5pbXBvd2VyQm94IC50aXRsZSB7CiAgZGlzcGxheTogbm9uZTsKfQouaW1wb3dlckJveCAuaW5mbyB7CiAgZGlzcGxheTogbm9uZTsKfQouc3RhdHVzX2ljb24gewogIGRpc3BsYXk6IG5vbmU7Cn0KLmltcG93ZXJCb3ggLnN0YXR1cyB7CiAgZGlzcGxheTogbm9uZTsKfQ==' + +interface WeixinLoginResponse { + uid: string + nickname: string + avatar: string + mobile: string + introduce: string + access_token: string + open_token: string + ret?: number + msg?: string +} + +interface TeamBasicInfo { + team_id: string | number + team_name: string + enable_invite: number + role: string + score: number +} + +const TEAM_STORAGE_KEY = 'team_id' +const TEAM_SCORE_KEY = 'team_score' + +const qrEl = document.getElementById('qr') as HTMLElement + +// 接口会返回未加引号的长整型 id,JSON.parse 会精度丢失甚至报错,先补引号 +function parseApiJson(text: string): T { + return JSON.parse(text.replace(/(\[[,:]\s*)(\d{16,})(\s*[\],}])/g, '$1"$2"$3')) as T +} + +function showQrMessage(message: string): void { + if (qrEl) qrEl.textContent = message +} + +function renderQrCode(): void { + if (!WECHAT_APPID) { + showQrMessage('缺少微信 AppID 配置') + return + } + if (typeof window.WxLogin !== 'function') { + showQrMessage('二维码加载失败,请刷新重试') + return + } + qrEl.textContent = '' + window.WxLogin({ + self_redirect: false, + id: 'qr', + appid: WECHAT_APPID, + scope: 'snsapi_login', + redirect_uri: encodeURIComponent(WECHAT_CALLBACK_URL), + state: 'laimiao', + href: LOGIN_QR_CSS, + }) +} + +// 只接受站内绝对路径,避免 ?redirect= 被当作开放重定向 +function redirectTarget(): string { + const redirect = new URLSearchParams(window.location.search).get('redirect') + if (redirect && redirect.startsWith('/') && !redirect.startsWith('//')) return redirect + return '/generate' +} + +async function cacheTeam(token: string): Promise { + try { + const res = await fetch(`${TEAM_API}/api/team/team_list`, { + headers: { Authorization: token }, + }) + if (!res.ok) return + const data = parseApiJson<{ ret?: number; msg?: string; data_list?: TeamBasicInfo[] }>( + await res.text(), + ) + if (data.ret !== undefined && data.ret !== 0) return + + const teams = data.data_list || [] + const personal = + teams.find((team) => team.enable_invite === 0 && team.team_name === '我的') || + teams.find((team) => team.enable_invite === 0) || + teams[0] + if (!personal) return + + localStorage.setItem(TEAM_STORAGE_KEY, String(personal.team_id)) + localStorage.setItem(TEAM_SCORE_KEY, String(personal.score ?? 0)) + } catch { + // 团队信息预热失败不阻断登录 + } +} + +async function completeWeixinLogin(code: string): Promise { + showQrMessage('正在登录…') + try { + const res = await fetch(`${TEAM_API}/api/user/weixin/login?code=${encodeURIComponent(code)}`, { + headers: { 'Content-Type': 'application/json' }, + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + + const data = parseApiJson(await res.text()) + if (data.ret !== undefined && data.ret !== 0) throw new Error(data.msg || '登录失败') + + localStorage.setItem('access_token', data.access_token) + if (data.open_token) localStorage.setItem('open_token', data.open_token) + localStorage.setItem( + 'user_info', + JSON.stringify({ + uid: data.uid, + nickname: data.nickname || '未知用户', + avatar: data.avatar || '', + mobile: data.mobile || '', + introduce: data.introduce || '', + }), + ) + + await cacheTeam(data.access_token) + window.location.href = redirectTarget() + } catch (error) { + console.error('登录失败:', error) + showQrMessage('登录失败,请刷新重试') + } +} + +const code = new URLSearchParams(window.location.search).get('code') +if (code) { + void completeWeixinLogin(code) +} else { + renderQrCode() +} diff --git a/me.html b/me.html new file mode 100644 index 0000000..a415d89 --- /dev/null +++ b/me.html @@ -0,0 +1,2722 @@ + + + + + + + + +个人中心|来秒AI + + + + + + + + + +
+ +

个人资料

管理您的个人信息和账户展示设置。

person
photo_camera

头像

支持 JPG, PNG, GIF 格式,最大 2MB。

昵称每 30 天可修改一次。

10001

账户信息

注册时间
2025年 01月 01日
绑定手机
138****8000已验证
+
+ + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..640e374 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2620 @@ +{ + "name": "laimiao-web-new", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "laimiao-web-new", + "version": "0.0.0", + "dependencies": { + "dompurify": "^3.4.17", + "marked": "^18.1.2", + "qrcode": "^1.5.4", + "vue": "^3.5.44", + "vue-router": "^5.4.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^24.13.3", + "@types/qrcode": "^1.5.6", + "@vitejs/plugin-vue": "^6.0.10", + "@vue/tsconfig": "^0.9.1", + "tailwindcss": "^4.3.3", + "typescript": "~5.9.3", + "vite": "^8.3.5", + "vue-tsc": "^3.3.11" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.11" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", + "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.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", + "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.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", + "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.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", + "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.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", + "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.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", + "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.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.10.tgz", + "integrity": "sha512-b80aUQh3n+qEzzZm5oQp3rT2QImeCx58K6eFl/RkJ3d65KW0kTuv5lNsLRVAjbW9YRUpo/wzmvzMgV34bTAItA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25 || ^3.6.0-rc.10" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue-macros/common": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.4.tgz", + "integrity": "sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.22", + "ast-kit": "^2.1.2", + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.44.tgz", + "integrity": "sha512-k34nCytbK8PQ1gR2mGROYOw6Pnv/mumqIdrZ3x2tbyXZDWm7ViKGLTfdB+DRrWqEQsLgGJvn0WN9jBif5zCTLQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.9", + "@vue/shared": "3.5.44", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.2" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.44.tgz", + "integrity": "sha512-W5AoBFWdw1lQAKiHowkfgFKFab82oSRT6sGsPfw/q1GRjsy/ZJFLHGhJyBRIFN9APLTUduDH9U2JAiPOIyKuYg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.44.tgz", + "integrity": "sha512-dzHmQ4jXTpdr5FjIQUeykVakN/uzqFbUo9/C5ikBFg3a3PeM6uRxeczx1ZbXBML0/zJstEh3ygYipzR5Z7b2Pw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.9", + "@vue/compiler-core": "3.5.44", + "@vue/compiler-dom": "3.5.44", + "@vue/compiler-ssr": "3.5.44", + "@vue/shared": "3.5.44", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.29", + "source-map-js": "^1.2.2" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.44.tgz", + "integrity": "sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz", + "integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.2.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", + "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.2.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", + "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.13.tgz", + "integrity": "sha512-axA0Xemn5LuUKuKWob0/kVq7xQ2mJJM6VqA9Exvt35nM9yRC31goWVve/G/Zb2ZCLsXC8FHYjjfXlnshknSCew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.44.tgz", + "integrity": "sha512-u+QJdKeBBEADkPdevsL+6E12QlYVfBD8Zcw5qjDQKtamI6l14KyqazJMobhCKelhWN5zpyKUiEDoTyHE93cqWw==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.44.tgz", + "integrity": "sha512-uEa/ndwjhrrhnOfb2p+sZy6zeoamJOISqaLSJGk/8R6nABT1ZldpzcJoPzKj+zHVomVBPcmQYyC2r4LtvDV6CQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.44.tgz", + "integrity": "sha512-3fc4ARcXVhVBaJ3ujWiNZJARD6pkyRcwG+ZYhMzjKtKUp7MAY0HLwr7r3Smep3Li6ZaWT6ewdmx8XqdaD5oNZg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.44", + "@vue/runtime-core": "3.5.44", + "@vue/shared": "3.5.44", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.44.tgz", + "integrity": "sha512-R4S1UoH4h5Frrauv8Z5XE5TxV9oBiUSgWIC4TyVK1FK+RocRnOLwFA5Xcmo2011RlgJp+AbZCnw6afrhlgRuUQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.44", + "@vue/runtime-dom": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.44.tgz", + "integrity": "sha512-H82XLH0DjmSOdbEwO15x3QTo1f5PWEtv237eLpYKc0mqLu+B8hnq16lFWmuBmNurkeiG3wr+oM1FhOll/YVFRg==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz", + "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 5.8", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.10.0.tgz", + "integrity": "sha512-KzvoxDfXNz2Gm8KYLh47K3qcY3Zza3WKOuh2xoqa7LBu6MDKWAZLovJtU9DuAd/Er+rhQHCRn619lHS5U+qmhw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0", + "ast-kit": "^3.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/helper-string-parser": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.8.tgz", + "integrity": "sha512-fVM+gSuKbdv9cpmyRNh5EvsDHUZCdxQH3FdnFKSkNYHpbsn95bbrxm+ES3MVeQjcqwi2O5ur/DjLfmTQaO60ng==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/helper-validator-identifier": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.6.tgz", + "integrity": "sha512-HYx0YmuchRxRSpnjvcNffnPS8DCauKNmnumIuHG3jnQAVewRA7kfJGwF13ayEpSZs7s4R/oBH4eppuXcAQd5JQ==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/parser": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.8.tgz", + "integrity": "sha512-Gq65JogAr0S2p0BzvfSJboyuiRnxDQApyOqHkKmkrvm+VlLK4gC3pKsiOKEdnhFBemrFbSnOu90mZZWDpiJnmQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/types": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.8.tgz", + "integrity": "sha512-iXSSZAKQWmaeAwGtFw10v9fg5hUiUs0L+w+Z2K11kym3pDNK+SF0VcAQPsEFvWcTSZ7LMgClMXDBvTbH0IlICg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.8", + "@babel/helper-validator-identifier": "^8.0.6" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/ast-kit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-3.0.0.tgz", + "integrity": "sha512-8OG92q3R35qjC/4i6BLBMg8IB+fClWu/1PEwg2Z9Rn+BuNaiEgJzpzn+pxWOdHJWDCAwu2JP0wCDTozAM4QirQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0", + "estree-walker": "^3.0.3", + "pathe": "^2.0.3" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dompurify": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", + "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.2.0.tgz", + "integrity": "sha512-h1pQPR9XoeSMdntRNCjyTznaume4UpfXrULBnlkZ2CUM/G5eSa+pBByGldO5uR8dxbPC+OuJXsPPNf+JZ81ADg==", + "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==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "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, + "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, + "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, + "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, + "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/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-string-ast": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz", + "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.19" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/marked": { + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.1.2.tgz", + "integrity": "sha512-DfOnvLAQ6WYeWxWO1u4RyFOeqyHup6dks5PPqnoFiooU7ipRvidO/2j8plhAHIiZg574E9ZlzSjUiPMLAzFkgg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "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/nostics": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/nostics/-/nostics-1.3.0.tgz", + "integrity": "sha512-wzRsrJwNlFbqIRQt8aCeL2WE8USip/uuvGSOZV1D02CxX8ABTMbw8IywKjH7yWHETMXbWJI4OBpihg9rmz2Vyg==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.4.0.tgz", + "integrity": "sha512-07Fys7iVx0X6TcviMOgogM1Lk33ZyqOezMnU79S+kffWqwolG/5W9V8EgQSeYCIfFTYVgssRbnH630Oa2+soBA==", + "license": "MIT", + "dependencies": { + "confbox": "^0.3.1", + "exsolve": "^1.1.3", + "pathe": "^2.0.3" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "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.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/rolldown": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.155.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "license": "MIT" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "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/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unplugin": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.7.0.tgz", + "integrity": "sha512-GrM2uq3DQo3hvLU2FjeYhooJ8BY9N6j1Fbzgx7Pta3VVJevsD3VP4dPjZkZOS0mLvN8w9zzr9OlP+31bSMyuLw==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@rolldown/pluginutils": "^1.0.1", + "picomatch": "^4.0.7", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rsbuild/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rsbuild/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vite": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", + "tinyglobby": "^0.2.17" + }, + "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.7.1 || ^0.8.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/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": 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.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "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/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "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/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "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/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "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/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "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/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "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/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "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/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "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/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "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/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "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/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "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/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.44.tgz", + "integrity": "sha512-dvvmaOCG7lAMkJTPqPwWeGMsPS6DIsFsfZDUgBp9V6mHf9b3+/wyVwYR4Es+WV6uJC0FKRxp81Obcq0ZwUcGFQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.44", + "@vue/compiler-sfc": "3.5.44", + "@vue/runtime-dom": "3.5.44", + "@vue/server-renderer": "3.5.44", + "@vue/shared": "3.5.44" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.4.0.tgz", + "integrity": "sha512-pFvcYcxinDHjZUlf7AzFBrT8r8Xqvv4uMHoJnjc+EqPawXQvHTkF8S4tfSmQLpriVHUGfkaKlI7Et0KRjTMTdw==", + "license": "MIT", + "dependencies": { + "@vue-macros/common": "^3.1.3", + "@vue/devtools-api": "^8.1.5", + "ast-walker-scope": "^0.10.0", + "chokidar": "^5.0.0", + "confbox": "^0.3.1", + "local-pkg": "^1.2.1", + "magic-string": "^1.4.3", + "mlly": "^1.8.2", + "muggle-string": "^0.4.1", + "nostics": "^1.3.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.7", + "scule": "^1.3.0", + "tinyglobby": "^0.2.17", + "unplugin": "^3.4.0", + "unplugin-utils": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@pinia/colada": ">=0.21.2", + "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", + "pinia": "^3.0.4 || ^4.0.2", + "vite": "^7.3.0 || ^8.0.0", + "vue": "^3.5.34 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@pinia/colada": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "pinia": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vue-router/node_modules/magic-string": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.5.tgz", + "integrity": "sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/vue-tsc": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.13.tgz", + "integrity": "sha512-jE4/bXdOMSHVxEGgQCqNllJy+9N3Gx6tfSAsg0yhFK/MAN6eXqISihupnDEKSCt12VVnjN2gI3AqLYcqoh5x/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.13" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "license": "MIT" + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9abb8b4 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "laimiao-web-new", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "build-only": "vite build", + "type-check": "vue-tsc -b", + "preview": "vite preview" + }, + "dependencies": { + "dompurify": "^3.4.17", + "marked": "^18.1.2", + "qrcode": "^1.5.4", + "vue": "^3.5.44", + "vue-router": "^5.4.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^24.13.3", + "@types/qrcode": "^1.5.6", + "@vitejs/plugin-vue": "^6.0.10", + "@vue/tsconfig": "^0.9.1", + "tailwindcss": "^4.3.3", + "typescript": "~5.9.3", + "vite": "^8.3.5", + "vue-tsc": "^3.3.11" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..9005123 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/industries/beauty/01.webp b/public/industries/beauty/01.webp new file mode 100644 index 0000000..c354dae Binary files /dev/null and b/public/industries/beauty/01.webp differ diff --git a/public/industries/beauty/02.webp b/public/industries/beauty/02.webp new file mode 100644 index 0000000..efd6c6d Binary files /dev/null and b/public/industries/beauty/02.webp differ diff --git a/public/industries/beauty/03.webp b/public/industries/beauty/03.webp new file mode 100644 index 0000000..526e49b Binary files /dev/null and b/public/industries/beauty/03.webp differ diff --git a/public/industries/beauty/04.webp b/public/industries/beauty/04.webp new file mode 100644 index 0000000..ba8449e Binary files /dev/null and b/public/industries/beauty/04.webp differ diff --git a/public/industries/beauty/05.webp b/public/industries/beauty/05.webp new file mode 100644 index 0000000..73bafe6 Binary files /dev/null and b/public/industries/beauty/05.webp differ diff --git a/public/industries/beauty/06.webp b/public/industries/beauty/06.webp new file mode 100644 index 0000000..a8d9336 Binary files /dev/null and b/public/industries/beauty/06.webp differ diff --git a/public/industries/beauty/07.webp b/public/industries/beauty/07.webp new file mode 100644 index 0000000..ac1f881 Binary files /dev/null and b/public/industries/beauty/07.webp differ diff --git a/public/industries/beauty/08.webp b/public/industries/beauty/08.webp new file mode 100644 index 0000000..0250cc0 Binary files /dev/null and b/public/industries/beauty/08.webp differ diff --git a/public/industries/ecommerce/01.webp b/public/industries/ecommerce/01.webp new file mode 100644 index 0000000..1c9c194 Binary files /dev/null and b/public/industries/ecommerce/01.webp differ diff --git a/public/industries/ecommerce/02.webp b/public/industries/ecommerce/02.webp new file mode 100644 index 0000000..12c97bd Binary files /dev/null and b/public/industries/ecommerce/02.webp differ diff --git a/public/industries/ecommerce/03.webp b/public/industries/ecommerce/03.webp new file mode 100644 index 0000000..c9ea7c0 Binary files /dev/null and b/public/industries/ecommerce/03.webp differ diff --git a/public/industries/ecommerce/04.webp b/public/industries/ecommerce/04.webp new file mode 100644 index 0000000..859aa1d Binary files /dev/null and b/public/industries/ecommerce/04.webp differ diff --git a/public/industries/ecommerce/05.webp b/public/industries/ecommerce/05.webp new file mode 100644 index 0000000..e1a522f Binary files /dev/null and b/public/industries/ecommerce/05.webp differ diff --git a/public/industries/ecommerce/06.webp b/public/industries/ecommerce/06.webp new file mode 100644 index 0000000..8055456 Binary files /dev/null and b/public/industries/ecommerce/06.webp differ diff --git a/public/industries/ecommerce/07.webp b/public/industries/ecommerce/07.webp new file mode 100644 index 0000000..d8f8196 Binary files /dev/null and b/public/industries/ecommerce/07.webp differ diff --git a/public/industries/ecommerce/08.webp b/public/industries/ecommerce/08.webp new file mode 100644 index 0000000..bce068a Binary files /dev/null and b/public/industries/ecommerce/08.webp differ diff --git a/public/industries/fashion/01.webp b/public/industries/fashion/01.webp new file mode 100644 index 0000000..bf564c3 Binary files /dev/null and b/public/industries/fashion/01.webp differ diff --git a/public/industries/fashion/02.webp b/public/industries/fashion/02.webp new file mode 100644 index 0000000..aab98a3 Binary files /dev/null and b/public/industries/fashion/02.webp differ diff --git a/public/industries/fashion/03.webp b/public/industries/fashion/03.webp new file mode 100644 index 0000000..bb73a05 Binary files /dev/null and b/public/industries/fashion/03.webp differ diff --git a/public/industries/fashion/04.webp b/public/industries/fashion/04.webp new file mode 100644 index 0000000..0d898dd Binary files /dev/null and b/public/industries/fashion/04.webp differ diff --git a/public/industries/fashion/05.webp b/public/industries/fashion/05.webp new file mode 100644 index 0000000..df6b09c Binary files /dev/null and b/public/industries/fashion/05.webp differ diff --git a/public/industries/fashion/06.webp b/public/industries/fashion/06.webp new file mode 100644 index 0000000..b9df29f Binary files /dev/null and b/public/industries/fashion/06.webp differ diff --git a/public/industries/fashion/07.webp b/public/industries/fashion/07.webp new file mode 100644 index 0000000..2741dbf Binary files /dev/null and b/public/industries/fashion/07.webp differ diff --git a/public/industries/fashion/08.webp b/public/industries/fashion/08.webp new file mode 100644 index 0000000..6299f8d Binary files /dev/null and b/public/industries/fashion/08.webp differ diff --git a/public/industries/food/01.webp b/public/industries/food/01.webp new file mode 100644 index 0000000..2e4ac8f Binary files /dev/null and b/public/industries/food/01.webp differ diff --git a/public/industries/food/02.webp b/public/industries/food/02.webp new file mode 100644 index 0000000..76ecffc Binary files /dev/null and b/public/industries/food/02.webp differ diff --git a/public/industries/food/03.webp b/public/industries/food/03.webp new file mode 100644 index 0000000..bf37b91 Binary files /dev/null and b/public/industries/food/03.webp differ diff --git a/public/industries/food/04.webp b/public/industries/food/04.webp new file mode 100644 index 0000000..ece08b2 Binary files /dev/null and b/public/industries/food/04.webp differ diff --git a/public/industries/food/05.webp b/public/industries/food/05.webp new file mode 100644 index 0000000..de8146c Binary files /dev/null and b/public/industries/food/05.webp differ diff --git a/public/industries/food/06.webp b/public/industries/food/06.webp new file mode 100644 index 0000000..3b7698b Binary files /dev/null and b/public/industries/food/06.webp differ diff --git a/public/industries/food/07.webp b/public/industries/food/07.webp new file mode 100644 index 0000000..3eaaf3d Binary files /dev/null and b/public/industries/food/07.webp differ diff --git a/public/industries/food/08.webp b/public/industries/food/08.webp new file mode 100644 index 0000000..8074aa3 Binary files /dev/null and b/public/industries/food/08.webp differ diff --git a/public/industries/home/01.webp b/public/industries/home/01.webp new file mode 100644 index 0000000..01821df Binary files /dev/null and b/public/industries/home/01.webp differ diff --git a/public/industries/home/02.webp b/public/industries/home/02.webp new file mode 100644 index 0000000..9ba2e0f Binary files /dev/null and b/public/industries/home/02.webp differ diff --git a/public/industries/home/03.webp b/public/industries/home/03.webp new file mode 100644 index 0000000..8942e4d Binary files /dev/null and b/public/industries/home/03.webp differ diff --git a/public/industries/home/04.webp b/public/industries/home/04.webp new file mode 100644 index 0000000..886e1ff Binary files /dev/null and b/public/industries/home/04.webp differ diff --git a/public/industries/home/05.webp b/public/industries/home/05.webp new file mode 100644 index 0000000..1af2229 Binary files /dev/null and b/public/industries/home/05.webp differ diff --git a/public/industries/home/06.webp b/public/industries/home/06.webp new file mode 100644 index 0000000..d625e79 Binary files /dev/null and b/public/industries/home/06.webp differ diff --git a/public/industries/home/07.webp b/public/industries/home/07.webp new file mode 100644 index 0000000..5f0c2bf Binary files /dev/null and b/public/industries/home/07.webp differ diff --git a/public/industries/home/08.webp b/public/industries/home/08.webp new file mode 100644 index 0000000..0875f20 Binary files /dev/null and b/public/industries/home/08.webp differ diff --git a/public/industries/tech/01.webp b/public/industries/tech/01.webp new file mode 100644 index 0000000..85456fd Binary files /dev/null and b/public/industries/tech/01.webp differ diff --git a/public/industries/tech/02.webp b/public/industries/tech/02.webp new file mode 100644 index 0000000..f74b06c Binary files /dev/null and b/public/industries/tech/02.webp differ diff --git a/public/industries/tech/03.webp b/public/industries/tech/03.webp new file mode 100644 index 0000000..80ecc6d Binary files /dev/null and b/public/industries/tech/03.webp differ diff --git a/public/industries/tech/04.webp b/public/industries/tech/04.webp new file mode 100644 index 0000000..2b4bf0e Binary files /dev/null and b/public/industries/tech/04.webp differ diff --git a/public/industries/tech/05.webp b/public/industries/tech/05.webp new file mode 100644 index 0000000..7aa8361 Binary files /dev/null and b/public/industries/tech/05.webp differ diff --git a/public/industries/tech/06.webp b/public/industries/tech/06.webp new file mode 100644 index 0000000..1b3c2a4 Binary files /dev/null and b/public/industries/tech/06.webp differ diff --git a/public/industries/tech/07.webp b/public/industries/tech/07.webp new file mode 100644 index 0000000..0e7ea8f Binary files /dev/null and b/public/industries/tech/07.webp differ diff --git a/public/industries/tech/08.webp b/public/industries/tech/08.webp new file mode 100644 index 0000000..84eafca Binary files /dev/null and b/public/industries/tech/08.webp differ diff --git a/public/industries/travel/01.webp b/public/industries/travel/01.webp new file mode 100644 index 0000000..223ce75 Binary files /dev/null and b/public/industries/travel/01.webp differ diff --git a/public/industries/travel/02.webp b/public/industries/travel/02.webp new file mode 100644 index 0000000..23e372d Binary files /dev/null and b/public/industries/travel/02.webp differ diff --git a/public/industries/travel/03.webp b/public/industries/travel/03.webp new file mode 100644 index 0000000..6bd8c52 Binary files /dev/null and b/public/industries/travel/03.webp differ diff --git a/public/industries/travel/04.webp b/public/industries/travel/04.webp new file mode 100644 index 0000000..0786aa0 Binary files /dev/null and b/public/industries/travel/04.webp differ diff --git a/public/industries/travel/05.webp b/public/industries/travel/05.webp new file mode 100644 index 0000000..3e3ca4f Binary files /dev/null and b/public/industries/travel/05.webp differ diff --git a/public/industries/travel/06.webp b/public/industries/travel/06.webp new file mode 100644 index 0000000..e018ade Binary files /dev/null and b/public/industries/travel/06.webp differ diff --git a/public/industries/travel/07.webp b/public/industries/travel/07.webp new file mode 100644 index 0000000..d5c3dd9 Binary files /dev/null and b/public/industries/travel/07.webp differ diff --git a/public/industries/travel/08.webp b/public/industries/travel/08.webp new file mode 100644 index 0000000..924e0b0 Binary files /dev/null and b/public/industries/travel/08.webp differ diff --git a/public/industries/travel/09.webp b/public/industries/travel/09.webp new file mode 100644 index 0000000..191bb6c Binary files /dev/null and b/public/industries/travel/09.webp differ diff --git a/public/industries/travel/10.webp b/public/industries/travel/10.webp new file mode 100644 index 0000000..5819d2a Binary files /dev/null and b/public/industries/travel/10.webp differ diff --git a/public/industries/travel/11.webp b/public/industries/travel/11.webp new file mode 100644 index 0000000..498682c Binary files /dev/null and b/public/industries/travel/11.webp differ diff --git a/public/industries/travel/12.webp b/public/industries/travel/12.webp new file mode 100644 index 0000000..90b4698 Binary files /dev/null and b/public/industries/travel/12.webp differ diff --git a/public/industries/travel/13.webp b/public/industries/travel/13.webp new file mode 100644 index 0000000..792f5ac Binary files /dev/null and b/public/industries/travel/13.webp differ diff --git a/public/industries/travel/14.webp b/public/industries/travel/14.webp new file mode 100644 index 0000000..752e6b8 Binary files /dev/null and b/public/industries/travel/14.webp differ diff --git a/public/industries/travel/15.webp b/public/industries/travel/15.webp new file mode 100644 index 0000000..a44dcfa Binary files /dev/null and b/public/industries/travel/15.webp differ diff --git a/public/industries/travel/16.webp b/public/industries/travel/16.webp new file mode 100644 index 0000000..98113b8 Binary files /dev/null and b/public/industries/travel/16.webp differ diff --git a/public/industries/travel/17.webp b/public/industries/travel/17.webp new file mode 100644 index 0000000..af5a62b Binary files /dev/null and b/public/industries/travel/17.webp differ diff --git a/public/industries/travel/18.webp b/public/industries/travel/18.webp new file mode 100644 index 0000000..83f38fb Binary files /dev/null and b/public/industries/travel/18.webp differ diff --git a/public/industries/travel/19.webp b/public/industries/travel/19.webp new file mode 100644 index 0000000..29c0493 Binary files /dev/null and b/public/industries/travel/19.webp differ diff --git a/public/industries/travel/20.webp b/public/industries/travel/20.webp new file mode 100644 index 0000000..098ee32 Binary files /dev/null and b/public/industries/travel/20.webp differ diff --git a/public/industries/travel/21.webp b/public/industries/travel/21.webp new file mode 100644 index 0000000..845c22f Binary files /dev/null and b/public/industries/travel/21.webp differ diff --git a/public/industries/travel/22.webp b/public/industries/travel/22.webp new file mode 100644 index 0000000..4183db1 Binary files /dev/null and b/public/industries/travel/22.webp differ diff --git a/public/industries/travel/23.webp b/public/industries/travel/23.webp new file mode 100644 index 0000000..38d3a00 Binary files /dev/null and b/public/industries/travel/23.webp differ diff --git a/public/industries/travel/24.webp b/public/industries/travel/24.webp new file mode 100644 index 0000000..9846822 Binary files /dev/null and b/public/industries/travel/24.webp differ diff --git a/public/industries/travel/25.webp b/public/industries/travel/25.webp new file mode 100644 index 0000000..fa67ae6 Binary files /dev/null and b/public/industries/travel/25.webp differ diff --git a/public/industries/travel/26.webp b/public/industries/travel/26.webp new file mode 100644 index 0000000..9692fc4 Binary files /dev/null and b/public/industries/travel/26.webp differ diff --git a/public/industries/travel/27.webp b/public/industries/travel/27.webp new file mode 100644 index 0000000..22858e1 Binary files /dev/null and b/public/industries/travel/27.webp differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..7c2aa3f --- /dev/null +++ b/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/src/account/AccountWidgets.vue b/src/account/AccountWidgets.vue new file mode 100644 index 0000000..f154f42 --- /dev/null +++ b/src/account/AccountWidgets.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/account/main.ts b/src/account/main.ts new file mode 100644 index 0000000..64dcd0c --- /dev/null +++ b/src/account/main.ts @@ -0,0 +1,9 @@ +import { createApp } from 'vue' +import AccountWidgets from './AccountWidgets.vue' +import '../ui/style.css' + +const host = document.createElement('div') +host.id = 'laimiao-account-widgets' +document.body.appendChild(host) + +createApp(AccountWidgets).mount(host) diff --git a/src/components/GenerateSidebar.vue b/src/components/GenerateSidebar.vue new file mode 100644 index 0000000..9583e0b --- /dev/null +++ b/src/components/GenerateSidebar.vue @@ -0,0 +1,503 @@ + + + + + \ No newline at end of file diff --git a/src/components/ImageLightbox.vue b/src/components/ImageLightbox.vue new file mode 100644 index 0000000..f6ad99e --- /dev/null +++ b/src/components/ImageLightbox.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/src/components/PaymentDialog.vue b/src/components/PaymentDialog.vue new file mode 100644 index 0000000..4030cab --- /dev/null +++ b/src/components/PaymentDialog.vue @@ -0,0 +1,243 @@ + + + diff --git a/src/components/RechargeDialog.vue b/src/components/RechargeDialog.vue new file mode 100644 index 0000000..0b3e868 --- /dev/null +++ b/src/components/RechargeDialog.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/src/components/ScoreDetailDialog.vue b/src/components/ScoreDetailDialog.vue new file mode 100644 index 0000000..facea36 --- /dev/null +++ b/src/components/ScoreDetailDialog.vue @@ -0,0 +1,259 @@ + + + + + diff --git a/src/components/Toast.vue b/src/components/Toast.vue new file mode 100644 index 0000000..77793ac --- /dev/null +++ b/src/components/Toast.vue @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/src/library/main.ts b/src/library/main.ts new file mode 100644 index 0000000..a84e3e8 --- /dev/null +++ b/src/library/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import AssetLibraryView from '../views/AssetLibraryView.vue' +import '../ui/style.css' + +createApp(AssetLibraryView).mount('#app') diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..def3c74 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,12 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' +import router from './router' + +const app = createApp(App) +app.use(router) + +router.isReady().then(() => { + app.mount('#app') + document.getElementById('app-boot')?.remove() +}) diff --git a/src/markdown/main.ts b/src/markdown/main.ts new file mode 100644 index 0000000..a6cbda8 --- /dev/null +++ b/src/markdown/main.ts @@ -0,0 +1,23 @@ +import { marked } from 'marked' +import DOMPurify from 'dompurify' + +const cache = new Map() + +function renderMarkdown(text: string): string { + const key = String(text || '') + const cached = cache.get(key) + if (cached !== undefined) return cached + + const html = DOMPurify.sanitize(marked.parse(key, { async: false, breaks: true }) as string) + if (cache.size > 200) cache.clear() + cache.set(key, html) + return html +} + +declare global { + interface Window { + __renderMarkdown?: (text: string) => string + } +} + +window.__renderMarkdown = renderMarkdown diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..4bfdb6c --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,42 @@ +import { createRouter, createWebHistory } from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' + +const routes: RouteRecordRaw[] = [ + { + path: '/', + alias: ['/app.html', '/app'], + name: 'home', + component: () => import('../views/HomeView.vue'), + meta: { + title: 'Mindora — Smarter Mental Health, Powered by AI', + description: + 'Mindora brings instant mental health insights, personalized advice, and virtual care, anytime, anywhere.', + }, + }, + { + path: '/:pathMatch(.*)*', + name: 'not-found', + component: () => import('../views/NotFoundView.vue'), + meta: { + title: 'Page not found — Mindora', + }, + }, +] + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes, + scrollBehavior(to) { + if (to.hash) { + return { el: to.hash, behavior: 'smooth' } + } + return { top: 0 } + }, +}) + +router.afterEach((to) => { + const title = to.meta.title as string | undefined + if (title) document.title = title +}) + +export default router diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..5e1e29b --- /dev/null +++ b/src/style.css @@ -0,0 +1,24 @@ +@import "tailwindcss"; + +@theme { + --font-display: 'Poppins', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + --font-serif: 'Brygada 1918', Georgia, "Times New Roman", serif; + --color-ink: #28311f; + --color-moss: #2c3a22; + --color-page: #eef0e4; +} + +@layer base { + html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + } + + body { + margin: 0; + background: var(--color-page); + color: var(--color-ink); + font-family: var(--font-display); + } +} diff --git a/src/ui/style.css b/src/ui/style.css new file mode 100644 index 0000000..3a052d8 --- /dev/null +++ b/src/ui/style.css @@ -0,0 +1,22 @@ +@import "tailwindcss"; + +@theme { + --font-display: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Helvetica Neue", "Microsoft YaHei", sans-serif; +} + +@layer base { + html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + } + + body { + font-family: var(--font-display); + } + + .material-symbols-outlined { + font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; + opacity: 1; + } +} diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 0000000..815899b --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,73 @@ +// 登录对接 laimiaoai 后端:登录页复用 laimiaoai-web 的 /login(微信扫码登录), +// 通过 app=tripcn 参数加载 tripcn 品牌界面;登录成功后携带 access_token 回跳。 + +const TOKEN_KEY = 'access_token' +const USER_INFO_KEY = 'user_info' + +// laimiaoai 登录页地址:dev 默认本地 laimiaoai dev server,test/prod 走 laimiaoai 域名 +const LAIMIAO_LOGIN_URL = import.meta.env.VITE_LAIMIAO_LOGIN_URL || '/login/' + +export function getToken(): string | null { + return localStorage.getItem(TOKEN_KEY) +} + +export function isLoggedIn(): boolean { + return !!getToken() +} + +// 生成页回跳地址(相对当前访问源,兼容 base 子路径部署) +export function getGenerateCallbackUrl(): string { + const basePath = import.meta.env.BASE_URL || '/' + const base = `${basePath.replace(/\/+$/, '')}/generate` + const search = window.location.search + return search ? `${base}${search}` : base +} + +// 跳转 laimiaoai 登录页(app=tripcn 品牌界面),携带回调地址;登录成功后带 token 回跳 +export function redirectToLogin(redirectUrl?: string): void { + const target = redirectUrl || getGenerateCallbackUrl() + window.location.href = `${LAIMIAO_LOGIN_URL}?redirect=${encodeURIComponent(target)}` +} + +// 判断后端返回是否为登录失效(ret=101010 / 未登录 / 登录超时) +export function isAuthErrorPayload(payload: unknown): boolean { + if (payload && typeof payload === 'object') { + const p = payload as { ret?: number; msg?: string } + if (p.ret === 101010) return true + if (typeof p.msg === 'string' && (p.msg.includes('未登录') || p.msg.includes('登录超时'))) return true + } + return false +} + +// 通过抛出的 Error 判断是否登录失效 +export function isAuthErrorMessage(error: unknown): boolean { + const msg = error instanceof Error ? error.message : String(error) + return msg.includes('未登录') || msg.includes('登录超时') +} + +// 登录失效处理:清除 token 并跳登录页,登录成功后回到生成页 +let authRedirecting = false +export function redirectToLoginForExpired(): void { + if (authRedirecting || window.location.pathname.startsWith('/login')) return + authRedirecting = true + localStorage.removeItem(TOKEN_KEY) + const target = getGenerateCallbackUrl() + window.location.href = `${LAIMIAO_LOGIN_URL}?redirect=${encodeURIComponent(target)}` +} + +// 兼容旧回调:从回调 URL 中提取并保存 token 与用户信息(外部登录回跳时携带) +export function consumeLoginCallback(query: Record): boolean { + const token = query.access_token + if (!token) return false + + localStorage.setItem(TOKEN_KEY, token) + if (query.user_info) { + try { + const parsed = JSON.parse(query.user_info) + localStorage.setItem(USER_INFO_KEY, JSON.stringify(parsed)) + } catch { + localStorage.setItem(USER_INFO_KEY, query.user_info) + } + } + return true +} \ No newline at end of file diff --git a/src/utils/docDisplay.ts b/src/utils/docDisplay.ts new file mode 100644 index 0000000..50cca57 --- /dev/null +++ b/src/utils/docDisplay.ts @@ -0,0 +1,29 @@ +// 文档展示辅助:素材库与生成页输入框的文档 chip 共用,保证图标/颜色/角标一致。 +// 扩展名优先取文件名(素材库名即上传时的原始文件名),缺失时回退从 URL 推导。 +// 生成页的调用形态是 name 缺失、仅剩 URL,故参数顺序为 name 在前、url 兜底。 + +function extFromDoc(name?: string, url?: string): string { + const src = (name && name.split('?')[0]) || (url || '').split('?')[0] + return src.match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase() || '' +} + +export function docExtLabel(name?: string, url?: string): string { + const ext = extFromDoc(name, url) + if (['pdf', 'doc', 'docx', 'txt', 'md'].includes(ext)) return ext.toUpperCase() + return 'DOC' +} + +export function docIconClass(name?: string, url?: string): { icon: string; color: string } { + const ext = extFromDoc(name, url) + if (ext === 'pdf') { + return { icon: 'picture_as_pdf', color: 'text-red-500' } + } else if (ext === 'doc' || ext === 'docx') { + return { icon: 'description', color: 'text-blue-500' } + } else if (ext === 'xls' || ext === 'xlsx') { + return { icon: 'table_chart', color: 'text-green-500' } + } else if (ext === 'ppt' || ext === 'pptx') { + return { icon: 'slideshow', color: 'text-orange-500' } + } else { + return { icon: 'insert_drive_file', color: 'text-gray-500' } + } +} diff --git a/src/utils/drawingWorks.ts b/src/utils/drawingWorks.ts new file mode 100644 index 0000000..be0b851 --- /dev/null +++ b/src/utils/drawingWorks.ts @@ -0,0 +1,44 @@ +import { getDrawingList, getTeamId, resolveFileUrl, type TeamDrawingItem } from './teamApi' + +export interface DrawingWork { + id: string + title: string + image: string + date: string + created: number +} + +function toDateStr(ts: number): string { + const d = new Date(ts * 1000) + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}` +} + +function toDrawingWork(item: TeamDrawingItem): DrawingWork { + return { + id: item.id, + title: item.workflow_name || `作品 ${item.id}`, + image: resolveFileUrl(item.drawing_url?.[0] || ''), + date: toDateStr(item.add_time), + created: Number(item.add_time) || 0 + } +} + +export async function loadDrawingWorks(pageSize = 100): Promise { + const teamId = await getTeamId() + const result = await getDrawingList(teamId, { pageSize }) + return (result.data_list || []).map(toDrawingWork) +} + +export interface DrawingWorksPage { + works: DrawingWork[] + total: number +} + +export async function loadDrawingWorksPage(page = 1, pageSize = 20): Promise { + const teamId = await getTeamId() + const result = await getDrawingList(teamId, { page, pageSize }) + return { + works: (result.data_list || []).map(toDrawingWork), + total: Number(result.total ?? 0), + } +} diff --git a/src/utils/graphAgent.ts b/src/utils/graphAgent.ts new file mode 100644 index 0000000..33c3691 --- /dev/null +++ b/src/utils/graphAgent.ts @@ -0,0 +1,263 @@ +export const GRAPH_AGENT_API = import.meta.env.VITE_AGENT_API_URL || 'http://localhost:8100' +const APP_TYPE = 0 + +import { isAuthErrorPayload, redirectToLoginForExpired } from './auth' + +function guardAuthPayload(data: T): T { + if (isAuthErrorPayload(data)) { + redirectToLoginForExpired() + throw new Error('登录已过期,请重新登录') + } + return data +} + +// 相对路径(如 /agent-api)补全为当前源的绝对 URL, 供 new URL() 使用 +function agentApiBase(): string { + if (GRAPH_AGENT_API.startsWith('http://') || GRAPH_AGENT_API.startsWith('https://')) { + return GRAPH_AGENT_API + } + return `${window.location.origin}${GRAPH_AGENT_API}` +} + +interface CreateSessionPayload { + uid?: string + team_id?: string + title?: string + type?: number +} + +export interface SessionResponse { + session_id: string + uid: string + team_id: string + title: string + last_mode: string + add_time: number + update_time: number + type?: number + canvas_data?: string +} + +interface SessionListResponse { + sessions: SessionResponse[] + total: number +} + +export async function createSessionApi(payload: CreateSessionPayload): Promise { + const res = await fetch(`${agentApiBase()}/sessions`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + uid: payload.uid || '0', + team_id: payload.team_id || '0', + title: payload.title || '', + type: payload.type ?? APP_TYPE, + }), + }) + if (!res.ok) { + throw new Error(`创建会话失败: ${res.status}`) + } + return guardAuthPayload((await res.json()) as SessionResponse) +} + +export async function listSessionsApi(teamId: string, type: number = APP_TYPE): Promise { + const url = new URL(`${agentApiBase()}/sessions`) + url.searchParams.set('team_id', teamId) + url.searchParams.set('type', String(type)) + url.searchParams.set('limit', '50') + url.searchParams.set('offset', '0') + const token = localStorage.getItem('access_token') || '' + if (token) { + const payload = token.split('.')[1] + try { + const json = atob(payload.replace(/-/g, '+').replace(/_/g, '/')) + const match = json.match(/"uid"\s*:\s*"?([0-9]+)"?/) + if (match) url.searchParams.set('uid', match[1]) + } catch { + // token 解析失败时不带 uid,交由后端按 token 身份过滤 + } + } + // 必须带 _t + no-store:否则 CDN/网关会缓存首次的会话列表响应, + // 导致侧边栏标题、会话数量变旧(与 getSessionApi、confirm 轮询保持一致) + url.searchParams.set('_t', String(Date.now())) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + ...(token ? { Authorization: `Bearer ${token}` } : {}), + }, + cache: 'no-store', + }) + if (!res.ok) { + throw new Error(`获取会话列表失败: ${res.status}`) + } + return guardAuthPayload((await res.json()) as SessionListResponse) +} + +export async function updateSessionApi(sessionId: string, title: string): Promise { + const res = await fetch(`${agentApiBase()}/sessions/${encodeURIComponent(sessionId)}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ title }), + }) + if (!res.ok) { + throw new Error(`更新会话失败: ${res.status}`) + } +} + +// 会话附属数据(canvas_data):用于保存二维码成品图等不落消息、但需随会话持久化的内容 +export async function updateSessionCanvasApi(sessionId: string, canvasData: string): Promise { + const res = await fetch(`${agentApiBase()}/sessions/${encodeURIComponent(sessionId)}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ canvas_data: canvasData }), + }) + if (!res.ok) { + throw new Error(`保存会话数据失败: ${res.status}`) + } +} + +export interface HistoryMessage { + id?: string | number + role: 'user' | 'assistant' + mode?: string + content?: string + media?: Array<{ type?: string; url?: string }> + params?: Record + status?: string + error?: string + add_time?: number + tool_steps?: Array<{ name: string; status: 'running' | 'done' | 'error'; model?: string | null; error?: string | null }> + thinking?: string +} + +export async function getSessionApi( + sessionId: string, + uid?: string, + teamId?: string, +): Promise<{ session: SessionResponse; messages: HistoryMessage[] } | null> { + const url = new URL(`${agentApiBase()}/sessions/${encodeURIComponent(sessionId)}`) + url.searchParams.set('mode', 'agent') + if (uid) url.searchParams.set('uid', uid) + if (teamId) url.searchParams.set('team_id', teamId) + url.searchParams.set('_t', String(Date.now())) + + const res = await fetch(url.toString(), { cache: 'no-store' }) + if (!res.ok) { + throw new Error(`获取会话失败: ${res.status}`) + } + return guardAuthPayload((await res.json()) as { session: SessionResponse; messages: HistoryMessage[] }) +} + +export interface StreamEvent { + type?: string + content?: string + tool_name?: string + tool?: string + input?: { model?: string; image_url?: string; [key: string]: unknown } + output?: { + error?: string + images?: string[] + image_url?: string + url?: string + urls?: string[] + title?: string + description?: string + items?: Array<{ url?: string; title?: string; description?: string }> + } + media?: Array<{ type?: string; url?: string }> + status?: string + error?: string + confirm_type?: string + summary?: Record + styles?: Array<{ id: string; label: string; desc?: string }> + pending_id?: string +} + +export async function streamAgentMessage( + sessionId: string, + message: string, + opts: { uid?: string; team_id?: string; reference_image_urls?: string[]; type?: number; is_allowed?: boolean }, + onEvent: (ev: StreamEvent) => void, + signal?: AbortSignal, +): Promise { + const url = `${agentApiBase()}/sessions/${encodeURIComponent(sessionId)}/agent/stream` + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + message, + reference_image_urls: opts.reference_image_urls || [], + uid: opts.uid || '0', + team_id: opts.team_id || '0', + type: opts.type ?? APP_TYPE, + // 用户是否允许将本会话生成作品用于宣传/图片搜索收录(随该条生成消息入库),0 假 1 真 + is_allowed: opts.is_allowed === true ? 1 : 0, + }), + signal, + }) + if (!res.ok || !res.body) { + throw new Error(`HTTP ${res.status}`) + } + + const reader = res.body.getReader() + const decoder = new TextDecoder() + let buffer = '' + + try { + while (true) { + const { done, value } = await reader.read() + if (done) break + buffer += decoder.decode(value, { stream: true }) + const lines = buffer.split('\n') + buffer = lines.pop() || '' + for (const line of lines) { + if (!line.startsWith('data: ')) continue + let data: StreamEvent | null = null + try { + data = JSON.parse(line.slice(6)) as StreamEvent + } catch { + data = null + } + if (data) { + onEvent(data) + } + } + } + } finally { + // 释放 reader 锁:未释放的流会长期占用连接并持有下游组件作用域(内存泄漏) + try { + reader.releaseLock() + } catch { + void 0 + } + } +} + +export async function confirmAgentApi( + sessionId: string, + payload: { approved: boolean; content?: string; confirmType?: string; pendingId?: string }, +): Promise { + const url = `${agentApiBase()}/sessions/${encodeURIComponent(sessionId)}/agent/confirm` + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + approved: payload.approved, + content: payload.content ?? '', + // 回传弹窗类型:后端据此校验确认是否已过期,避免恢复值绑定到错误的中断 + confirm_type: payload.confirmType ?? '', + // 回传弹窗唯一 id:两次同类型弹窗仅凭 confirm_type 无法区分, + // 未回传/不匹配的旧弹窗确认会被后端拒绝(409),避免绑定到已取消的旧中断 + pending_id: payload.pendingId ?? '', + }), + }) + if (!res.ok) { + const body = (await res.json().catch(() => null)) as { detail?: unknown } | null + const detail = typeof body?.detail === 'string' ? body.detail : '' + const err = new Error(detail || `确认失败: ${res.status}`) + ;(err as Error & { status?: number }).status = res.status + throw err + } +} \ No newline at end of file diff --git a/src/utils/ossUpload.ts b/src/utils/ossUpload.ts new file mode 100644 index 0000000..fe0a3b3 --- /dev/null +++ b/src/utils/ossUpload.ts @@ -0,0 +1,82 @@ +// 直传 OSS:先向后端申请预签名上传凭证,浏览器再直接 PUT 到阿里云 OSS,不经过业务服务器中转 +const BASE = import.meta.env.VITE_TEAM_API_URL || 'http://localhost:8000' + +export interface OssUploadCredential { + put_url: string + img_url: string + content_type?: string +} + +async function getUploadCredential(file: File, category: string, signal?: AbortSignal): Promise { + const filename = file.name || 'Untitled.png' + const url = new URL(`${BASE}/api/base/upload_url`) + url.searchParams.set('filename', filename) + url.searchParams.set('category', category) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: { Authorization: localStorage.getItem('access_token') || '' }, + signal, + }) + if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`) + const data = await res.json() + if (data.ret !== undefined && data.ret !== 0) throw new Error(data.msg || '获取上传凭证失败') + if (!data.put_url || !data.img_url) throw new Error('上传凭证无效') + // 生产为 https 站点,http OSS URL 会被浏览器按混合内容静默拦截,强制升级 + const putUrl = data.put_url.replace(/^http:\/\//, 'https://') + return { ...data, put_url: putUrl } as OssUploadCredential +} + +export async function directUploadImage( + file: File, + category = 'creation', + onProgress?: (percent: number) => void, + signal?: AbortSignal, +): Promise { + const credential = await getUploadCredential(file, category, signal) + + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.open('PUT', credential.put_url) + xhr.setRequestHeader('Content-Type', credential.content_type || file.type || 'application/octet-stream') + if (onProgress) { + xhr.upload.onprogress = (ev) => { + if (ev.lengthComputable) { + onProgress(Math.min(99, Math.round((ev.loaded / ev.total) * 100))) + } + } + } + if (signal) { + if (signal.aborted) { + reject(new DOMException('上传已取消', 'AbortError')) + return + } + signal.addEventListener('abort', () => xhr.abort(), { once: true }) + } + xhr.onerror = () => reject(new Error('直传 OSS 失败,网络错误')) + xhr.onload = () => { + if (xhr.status < 200 || xhr.status >= 300) { + reject(new Error(`直传 OSS 失败,HTTP ${xhr.status}`)) + return + } + resolve(credential.img_url) + } + xhr.send(file) + }) +} + +// 直传不可用(旧后端/凭证接口异常)时兜底走服务器中转上传 +export async function uploadImageDirectOrRelay( + file: File, + category = 'company', + onProgress?: (percent: number) => void, + signal?: AbortSignal, +): Promise { + try { + return await directUploadImage(file, category, onProgress, signal) + } catch (error) { + if (error instanceof DOMException && error.name === 'AbortError') throw error + const { uploadImageApi } = await import('./teamApi') + return uploadImageApi(file, category, onProgress, signal) + } +} diff --git a/src/utils/payApi.ts b/src/utils/payApi.ts new file mode 100644 index 0000000..34da108 --- /dev/null +++ b/src/utils/payApi.ts @@ -0,0 +1,124 @@ +// Pay API wrapper — 与 laimiao-web payApi.ts 对齐,复用 team-api 的 /pay 路由 + +const BASE = import.meta.env.VITE_TEAM_API_URL || 'http://localhost:8000' + +export interface FeatureItem { + flag?: string + [key: string]: any +} + +export type Feature = string | FeatureItem + +export interface GoodsItem { + vip_level: number + vip_name: string + price: number + unit: string + txt_list: Feature[] + button: string + tip: string + month: number + score: number + extra_price?: number +} + +export interface GoodsListResponse { + data_list: GoodsItem[] +} + +export interface CreateOrderParams { + vip_level: number + price: number + score: number + team_id: string + month: number + type: number +} + +export interface CreateOrderResponse { + order_id: string +} + +export interface WeixinQrpayResponse { + code_url: string + order_id: string +} + +export interface AlipayPageResponse { + order_string: string + order_id: string +} + +export interface PayQueryResponse { + ispay: boolean + order_id: string +} + +function headers(): Record { + return { + 'Content-Type': 'application/json', + Authorization: localStorage.getItem('access_token') || '' + } +} + +async function parseJsonSafe(res: Response): Promise { + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + const data = await res.json() + if (data && typeof data === 'object' && data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + return data as T +} + +export async function getGoodsListApi(vipType: string, members: number = 1): Promise { + const url = new URL(`${BASE}/api/pay/goods_list`) + url.searchParams.set('vip_type', vipType) + url.searchParams.set('members', String(members)) + const res = await fetch(url.toString(), { method: 'GET', headers: headers() }) + return parseJsonSafe(res) +} + +export async function createOrderApi(params: CreateOrderParams): Promise { + const res = await fetch(`${BASE}/api/pay/create_order`, { + method: 'POST', + headers: headers(), + body: JSON.stringify(params) + }) + return parseJsonSafe(res) +} + +export async function weixinQrpayApi(orderId: string): Promise { + const res = await fetch(`${BASE}/api/pay/weixin/qrpay`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ order_id: orderId }) + }) + return parseJsonSafe(res) +} + +export async function alipayPageApi(orderId: string): Promise { + const res = await fetch(`${BASE}/api/pay/alipay/page`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ order_id: orderId }) + }) + return parseJsonSafe(res) +} + +export async function weixinPayQueryApi(orderId: string): Promise { + const res = await fetch(`${BASE}/api/pay/weixin/payquery/${encodeURIComponent(orderId)}`, { + method: 'GET', + headers: headers() + }) + return parseJsonSafe(res) +} + +export async function alipayPayQueryApi(orderId: string): Promise { + const res = await fetch(`${BASE}/api/pay/alipay/payquery/${encodeURIComponent(orderId)}`, { + method: 'GET', + headers: headers() + }) + return parseJsonSafe(res) +} diff --git a/src/utils/proxiedUrl.ts b/src/utils/proxiedUrl.ts new file mode 100644 index 0000000..86eb87f --- /dev/null +++ b/src/utils/proxiedUrl.ts @@ -0,0 +1,29 @@ +const IMAGE_PROXY = import.meta.env.VITE_IMAGE_PROXY === 'true' + +export function proxiedUrl(url: string): string { + if (!url || !/^https?:\/\//.test(url)) return url + if (!IMAGE_PROXY) return url + if (url.includes('laimiaoai.com') || url.includes('laimiao')) { + return `/img?u=${encodeURIComponent(url)}` + } + return url +} + +// 阿里云 CDN 开启了「自适应 WEBP」:只要请求头 Accept 含 image/webp(浏览器 必带), +// CDN 就会把图片自动转成 webp 并缓存,导致浏览器「图片另存为」得到 webp。 +// `x-oss-process=image/format,jpg` 会被自适应 WEBP 覆盖,必须用 CDN 侧的 `image_process=format,jpg`。 +// 仅用于「点击查看/另存」的大图,避免缩略图也退回体积更大的 jpg。 +function appendJpgProcess(url: string): string { + if (!url || !/^https?:\/\//i.test(url)) return url + if (!url.includes('laimiaoai.com')) return url + if (/[?&]image_process=/.test(url)) return url + return `${url}${url.includes('?') ? '&' : '?'}image_process=format,jpg` +} + +// 已是开发同源代理地址(/img?u=...)时,改写内层真实 URL,保证参数落到源站请求上 +export function withJpgFormat(url: string): string { + if (!url) return url + const m = url.match(/^\/img\?u=([^&]+)/) + if (m) return `/img?u=${encodeURIComponent(appendJpgProcess(decodeURIComponent(m[1])))}` + return appendJpgProcess(url) +} \ No newline at end of file diff --git a/src/utils/sessionStore.ts b/src/utils/sessionStore.ts new file mode 100644 index 0000000..a903806 --- /dev/null +++ b/src/utils/sessionStore.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue' +import type { SessionResponse } from './graphAgent' + +// GenerateSidebar 在生成页/资产库/个人中心都会挂载,共享同一份会话列表可避免 +// 每次切换视图都重新请求接口导致列表刷新闪烁。 +export const sessionList = ref([]) +export const sessionsLoaded = ref(false) + +export function setSessionList(list: SessionResponse[]): void { + sessionList.value = list +} + +export function upsertSession(session: SessionResponse): void { + if (!session?.session_id) return + sessionList.value = [session, ...sessionList.value.filter((s) => s.session_id !== session.session_id)] +} diff --git a/src/utils/teamApi.ts b/src/utils/teamApi.ts new file mode 100644 index 0000000..eb4c5a7 --- /dev/null +++ b/src/utils/teamApi.ts @@ -0,0 +1,944 @@ +// Team API wrapper for tripcn.ai-web +// All library API calls must include app_type=1 (tripcn uses 1, laimiaoai uses 0) + +import { proxiedUrl, withJpgFormat } from './proxiedUrl' +import { isAuthErrorPayload, redirectToLoginForExpired } from './auth' + +const BASE = import.meta.env.VITE_TEAM_API_URL || 'http://localhost:8000' +const APP_TYPE = 0 + +/** + * Prefix relative file URLs with the API base host, since the backend returns paths like `/img/xxx.jpg`. + */ +export function resolveFileUrl(url?: string): string { + if (!url) return '' + if (/^https?:\/\//i.test(url) || url.startsWith('data:') || url.startsWith('blob:')) { + return proxiedUrl(url) + } + return proxiedUrl(`${BASE}${url.startsWith('/') ? '' : '/'}${url}`) +} + +// 匹配图片 CDN 的尺寸后缀:/{size} 或 /s.{size}(正方形裁剪) +const IMAGE_SUFFIX_RE = + /(\/(?:s\.)?(?:xxxs|xxs|xs|s|m|l|xl|xxl|xxxl))(?=\?|$)|(\/s\.(?:xxxs|xxs|xs|s|m|l|xl|xxl|xxxl))(?=\?|$)/ + +function stripImageSuffix(url: string): string { + return url.replace(IMAGE_SUFFIX_RE, '') +} + +// /xxs 是图片 CDN 的尺寸后缀(100px 宽),由后端按需生成缩略图 +export function getLibraryThumbUrl(url?: string): string { + if (!url) return '' + return withJpgFormat(resolveFileUrl(`${stripImageSuffix(url)}/xxs`)) +} + +// 素材入库需要真实宽高,后端据此生成 ext_info(last_img_list/file_number) +export function getFileDimensions(file: File): Promise<{ width: number; height: number }> { + return new Promise((resolve) => { + if (!file.type.startsWith('image/')) { + resolve({ width: 0, height: 0 }) + return + } + const img = new Image() + img.onload = () => resolve({ width: img.width, height: img.height }) + img.onerror = () => resolve({ width: 0, height: 0 }) + img.src = URL.createObjectURL(file) + }) +} + +function headers(): Record { + return { + 'Content-Type': 'application/json', + Authorization: localStorage.getItem('access_token') || '' + } +} + +function parseJsonText(text: string): T { + const safe = text.replace(/([\[:,]\s*)(\d{16,})(\s*[,\]}])/g, '$1"$2"$3') + return JSON.parse(safe) as T +} + +async function parseJsonSafe(res: Response): Promise { + const data = parseJsonText(await res.text()) + if (isAuthErrorPayload(data)) { + redirectToLoginForExpired() + } + return data +} + +export interface UploadImageResponse { + img_url: string + category: string + ret?: number + msg?: string +} + +/** + * 上传头像(与 laimiaoai-web 的 baseApi.uploadImageApi 对齐): + * 返回 { img_url } 对象,调用方取 res.img_url。 + */ +export async function uploadAvatarApi(file: File): Promise { + const formData = new FormData() + formData.append('imgfile', file) + formData.append('category', 'avatar') + + const res = await fetch(`${BASE}/api/base/imgupload`, { + method: 'POST', + headers: { + Authorization: localStorage.getItem('access_token') || '' + }, + body: formData, + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '上传失败') + } + if (!data.img_url) { + throw new Error('上传失败') + } + + return data +} + +export interface WeixinLoginResponse { + uid: string + nickname: string + avatar: string + mobile: string + introduce: string + access_token: string + open_token: string + ret?: number + msg?: string +} + +export async function userWeixinLoginApi(code: string): Promise { + const url = new URL(`${BASE}/api/user/weixin/login`) + url.searchParams.set('code', code) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '登录失败') + } + + return data +} + +export interface TeamLibraryItem { + id: string + team_id: string + uid: string + path: string + name: string + type: number // 0=directory, 1=file + url: string + ext_info: Record + add_time: number +} + +export interface TeamLibraryListResponse { + ret: number + msg?: string + items: TeamLibraryItem[] + total: number + page: number + page_size: number +} + +export interface TeamDrawingItem { + id: string + team_id: string + workflow_id: string + workflow_name: string + prompt_id: string + is_like: boolean + like_count: number + download_count: number + share_count: number + drawing_url: string[] + drawing_type: string + drawing_tag: string + image_height: number + image_width: number + image_rgb: string + file_size: number + add_time: number + user_info: Record +} + +export interface TeamDrawingListResponse { + ret: number + msg?: string + data_list: TeamDrawingItem[] + total: number + page: number + page_size: number +} + +export interface CreateLibraryRequest { + id?: number + team_id: number | string + parent_id: number | string + path?: string + name: string + type: number // 0=directory, 1=file + app_type?: number + url?: string + width?: number + height?: number + file_type?: string // 'img' | 'video' | 'audio' | 'text' + file_size?: number +} + +export interface CreateLibraryResponse { + ret: number + msg?: string + id: string +} + +export interface DeleteLibraryResponse { + ret: number + msg?: string +} + +/** + * Get trip team info (team_id and score) + * Also stores team_id and score in localStorage + */ +/** + * Get team library list + * @param teamId Team ID + * @param parentId Parent folder ID (0 for root) + * @param page Page number (default 1) + * @param pageSize Page size (default 20) + */ +export async function getLibraryList( + teamId: string | number, + parentId: string | number, + page: number = 1, + pageSize: number = 20, + itemType?: number +): Promise { + const url = new URL(`${BASE}/api/team/library`) + url.searchParams.set('team_id', String(teamId)) + url.searchParams.set('parent_id', String(parentId)) + url.searchParams.set('page', String(page)) + url.searchParams.set('page_size', String(pageSize)) + url.searchParams.set('app_type', String(APP_TYPE)) + if (itemType !== undefined) { + url.searchParams.set('item_type', String(itemType)) + } + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers() + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: TeamLibraryListResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +export interface ScoreDetailItem { + id: string + team_id: string + uid: string + nickname: string + avatar: string + score: number + score_text: string + add_time: number + type: number + run_time: number +} + +export interface ScoreDetailListResponse { + data_list: ScoreDetailItem[] +} + +/** + * Get trip score detail list (app_type=1) + */ +export async function getScoreDetailApi( + teamId: string | number, + page: number = 1, + pageSize: number = 20 +): Promise { + const url = new URL(`${BASE}/api/team/score_detail`) + url.searchParams.set('team_id', String(teamId)) + url.searchParams.set('app_type', String(APP_TYPE)) + url.searchParams.set('page', String(page)) + url.searchParams.set('page_size', String(pageSize)) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers() + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + return parseJsonSafe(res) +} + +/** + * Get team drawing list (作品列表) + */ +export async function getDrawingList( + teamId: string | number, + options: { + timeRange?: string + mediaType?: string + tag?: string + page?: number + pageSize?: number + } = {} +): Promise { + const url = new URL(`${BASE}/api/team/drawing_list`) + url.searchParams.set('team_id', String(teamId)) + url.searchParams.set('app_type', String(APP_TYPE)) + if (options.timeRange) url.searchParams.set('time_range', options.timeRange) + if (options.mediaType) url.searchParams.set('media_type', options.mediaType) + if (options.tag) url.searchParams.set('tag', options.tag) + url.searchParams.set('page', String(options.page || 1)) + url.searchParams.set('page_size', String(options.pageSize || 20)) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers() + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: TeamDrawingListResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +/** Create a drawing (作品) from a client-composed image */ +export interface CreateDrawingRequest { + team_id: number | string + drawing_url: string[] + drawing_type?: string + drawing_tag?: string + image_width?: number + image_height?: number + file_size?: number + app_type?: number +} + +export interface CreateDrawingResponse { + ret?: number + msg?: string + id: string +} + +export async function createDrawingApi(data: CreateDrawingRequest): Promise { + const res = await fetch(`${BASE}/api/team/drawing`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ + app_type: APP_TYPE, + ...data + }) + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const result: CreateDrawingResponse = await parseJsonSafe(res) + + if (result.ret !== undefined && result.ret !== 0) { + throw new Error(result.msg || '请求失败') + } + + return result +} + +export function uploadImageApi( + imgfile: File, + category = 'company', + onProgress?: (percent: number) => void, + signal?: AbortSignal, +): Promise { + const formData = new FormData() + formData.append('imgfile', imgfile) + formData.append('category', category) + + // XHR 代替 fetch:支持上传进度回调(onProgress 可选,不传行为与原来一致) + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.open('POST', `${BASE}/api/base/imgupload`) + xhr.setRequestHeader('Authorization', localStorage.getItem('access_token') || '') + if (signal) { + if (signal.aborted) { + reject(new DOMException('上传已取消', 'AbortError')) + return + } + signal.addEventListener('abort', () => xhr.abort(), { once: true }) + } + if (onProgress) { + xhr.upload.onprogress = (ev) => { + if (ev.lengthComputable) { + onProgress(Math.min(99, Math.round((ev.loaded / ev.total) * 100))) + } + } + } + xhr.onerror = () => reject(new Error('网络错误,上传失败')) + xhr.onabort = () => reject(new Error('上传已取消')) + xhr.onload = () => { + if (xhr.status < 200 || xhr.status >= 300) { + reject(new Error(`HTTP error! status: ${xhr.status}`)) + return + } + let data: { img_url?: string; ret?: number; msg?: string } + try { + data = parseJsonText(xhr.responseText) + } catch { + data = {} + } + if (data.ret !== undefined && data.ret !== 0) { + reject(new Error(data.msg || '请求失败')) + return + } + if (!data.img_url) { + reject(new Error('上传失败')) + return + } + resolve(data.img_url) + } + xhr.send(formData) + }) +} + +/** + * Create library item (folder or file) + * @param data Library item data + */ +export async function createLibrary(data: CreateLibraryRequest): Promise { + const res = await fetch(`${BASE}/api/team/library`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ + ...data, + app_type: APP_TYPE + }) + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const result: CreateLibraryResponse = await parseJsonSafe(res) + + if (result.ret !== undefined && result.ret !== 0) { + throw new Error(result.msg || '请求失败') + } + + return result +} + +// Backend `ids` is a List[str] query param: comma-joined values parse as one +// element and are silently skipped while the API still returns ret=0. +export async function deleteLibrary(ids: string[]): Promise { + const url = new URL(`${BASE}/api/team/library`) + for (const id of ids) { + if (id) url.searchParams.append('ids', id) + } + + const res = await fetch(url.toString(), { + method: 'DELETE', + headers: headers() + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: DeleteLibraryResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +/** + * Get team_id from localStorage or API + * Priority: localStorage trip_team_id > user_info.team_id > API call + */ +export async function getTeamId(): Promise { + const cached = localStorage.getItem('team_id') || localStorage.getItem('trip_team_id') + if (cached) return cached + + try { + const info = JSON.parse(localStorage.getItem('user_info') || '{}') + if (info && info.team_id) { + localStorage.setItem('team_id', String(info.team_id)) + return String(info.team_id) + } + } catch { + // user_info 损坏时继续走接口兜底 + } + + const team = await getMyTeam() + return team ? String(team.team_id) : '0' +} + +export interface MyTeam { + team_id: string + team_name: string + score: number + enable_invite?: number +} + +export async function getMyTeam(): Promise { + const token = localStorage.getItem('access_token') || '' + if (!token) return null + + try { + const res = await fetch(`${BASE}/api/team/team_list`, { headers: headers() }) + if (!res.ok) return null + const data = await parseJsonSafe<{ data_list?: MyTeam[]; ret?: number; msg?: string }>(res) + if (data.ret !== undefined && data.ret !== 0) return null + + const teams = data.data_list || [] + const personal = + teams.find((team) => team.enable_invite === 0 && team.team_name === '我的') || + teams.find((team) => team.enable_invite === 0) || + teams[0] + if (!personal) return null + + localStorage.setItem('team_id', String(personal.team_id)) + localStorage.setItem('team_score', String(personal.score ?? 0)) + return personal + } catch { + return null + } +} + +export interface UserProfile { + uid: string + nickname: string + avatar: string + mobile: string + introduce: string + active_time: number + add_time: number + update_time: number + weixin_openid: string + access_token: string + open_token: string +} + +/** + * Get current user profile (/api/user/me) + */ +export async function getUserMe(): Promise { + const res = await fetch(`${BASE}/api/user/me`, { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: UserProfile & { ret?: number; msg?: string } = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +/** + * Update current user profile (/api/user/update) + */ +export async function updateUserProfile(payload: { + nickname: string + avatar: string + introduce: string +}): Promise { + const res = await fetch(`${BASE}/api/user/update`, { + method: 'PUT', + headers: headers(), + body: JSON.stringify(payload), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: UserProfile & { ret?: number; msg?: string } = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +export interface InviteLinkResponse { + ret?: number + msg?: string + invite_link: string +} + +/** + * 生成 / 获取当前用户的专属邀请链接(个人邀请,不带 team_id)。 + * 后端返回形如 `/invite/{邀请码}` 的相对路径。 + */ +export async function getInviteLink(): Promise { + const res = await fetch(`${BASE}/api/team/invite`, { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: InviteLinkResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +export interface InviterItem { + uid: string + nickname: string + avatar: string + mobile: string + introduce: string + active_time: number + add_time: number + update_time: number +} + +export interface InviterListResponse { + ret?: number + msg?: string + data_list: InviterItem[] +} + +/** + * 当前用户作为邀请人时,已成功邀请的用户列表(邀请记录)。 + */ +export async function getInviterList(): Promise { + const res = await fetch(`${BASE}/api/user/inviter_list`, { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: InviterListResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +export interface InviteRoleResponse { + ret?: number + msg?: string + role: string + nickname: string + avatar: string +} + +/** + * 查询邀请码对应的主体类型(`user` 个人邀请 / `team` 团队邀请)。 + */ +export async function getInviteRole(code: string): Promise { + const url = new URL(`${BASE}/api/user/invite/role`) + url.searchParams.set('code', code) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: InviteRoleResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +export interface SimpleResultResponse { + ret?: number + msg?: string +} + +/** 获取短信验证码应答(/api/user/get_signin_code):minute 为倒计时分钟数 */ +export interface SigninCodeResponse extends SimpleResultResponse { + mobile?: string + minute?: number +} + +/** + * 接受个人邀请(绑定邀请人)。 + */ +export async function acceptInvitation(code: string): Promise { + const res = await fetch(`${BASE}/api/user/invitation/invite`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ code }), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: SimpleResultResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +/** + * 发送手机短信验证码(与 laimiaoai-web 的 teamApi.getSigninCodeApi 对齐)。 + * 同时用于旅游机构认证与微信登录后绑定手机号。 + */ +export async function getSigninCodeApi(mobile: string): Promise { + const url = new URL(`${BASE}/api/user/get_signin_code`) + url.searchParams.set('mobile', mobile) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: SigninCodeResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +/** + * 绑定手机号(与 laimiaoai-web 的 userBindMobileApi 对齐),需登录。 + * 校验通过后后端把 mobile 写入当前用户。 + */ +export async function bindMobileApi(mobile: string, code: string): Promise { + const res = await fetch(`${BASE}/api/user/bind`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ mobile, code }), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: SimpleResultResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '绑定失败') + } + + return data +} + +export interface UploadCertificationRequest { + team_id: string + company_name: string + company_code: string + business_license: string + contact_name: string + contact_station: string + contact_rank?: string + contact_mobile: string + auth_code: string + code: string +} + +export interface UploadCertificationResponse { + ret?: number + msg?: string + state?: number + code?: string | number +} + +/** + * 提交旅游机构(企业)认证资料(与 laimiaoai-web 的 teamApi.uploadCertificationApi 对齐)。 + */ +export async function uploadCertificationApi(params: UploadCertificationRequest): Promise { + const res = await fetch(`${BASE}/api/company/upload`, { + method: 'POST', + headers: headers(), + body: JSON.stringify({ + app_type: APP_TYPE, + ...params, + }), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: UploadCertificationResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +/** + * 校验统一社会信用代码是否已被其他团队认证或正在认证。 + * 命中冲突时后端返回非 0 ret,这里抛出携带提示文案的 Error。 + */ +export async function checkCompanyCodeApi( + companyCode: string, + teamId: string, + appType: number = APP_TYPE, +): Promise { + const url = new URL(`${BASE}/api/company/check_code`) + url.searchParams.set('company_code', companyCode) + url.searchParams.set('team_id', teamId) + url.searchParams.set('app_type', String(appType)) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: SimpleResultResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '该营业执照已经认证') + } + + return data +} + +export interface CompanyDetail { + company_name: string + company_code: string + business_license: string + contact_name: string + contact_station: string + contact_rank: string + contact_mobile: string +} + +/** + * 查询已提交的认证资料详情。 + */ +export async function getCompanyDetailApi(teamId: string): Promise { + const url = new URL(`${BASE}/api/company/detail`) + url.searchParams.set('team_id', teamId) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: CompanyDetail & { ret?: number; msg?: string } = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} + +export interface CompanyStateResponse { + ret?: number + msg?: string + state: number + code?: string | number +} + +/** + * 查询认证状态:0 待审核 / 1 审核通过 / 2 审核不通过。 + */ +export async function getCompanyStateApi(teamId: string): Promise { + const url = new URL(`${BASE}/api/company/state`) + url.searchParams.set('team_id', teamId) + + const res = await fetch(url.toString(), { + method: 'GET', + headers: headers(), + }) + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`) + } + + const data: CompanyStateResponse = await parseJsonSafe(res) + + if (data.ret !== undefined && data.ret !== 0) { + throw new Error(data.msg || '请求失败') + } + + return data +} \ No newline at end of file diff --git a/src/views/AssetLibraryView.vue b/src/views/AssetLibraryView.vue new file mode 100644 index 0000000..24bfeb4 --- /dev/null +++ b/src/views/AssetLibraryView.vue @@ -0,0 +1,1184 @@ + + + + + \ No newline at end of file diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue new file mode 100644 index 0000000..a122aca --- /dev/null +++ b/src/views/HomeView.vue @@ -0,0 +1,1003 @@ + + + + + diff --git a/src/views/NotFoundView.vue b/src/views/NotFoundView.vue new file mode 100644 index 0000000..09515f4 --- /dev/null +++ b/src/views/NotFoundView.vue @@ -0,0 +1,75 @@ + + + diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..1b81782 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,19 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + "allowArbitraryExtensions": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "login/**/*.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..8455dcb --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "module": "nodenext", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..786f37b --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,85 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath, URL } from 'node:url' +import tailwindcss from '@tailwindcss/vite' +import vue from '@vitejs/plugin-vue' +import { defineConfig, loadEnv, type Plugin } from 'vite' + +// 支持 /generate、/me、/library 这类不带 .html 的页面地址(dev 与 preview) +function extensionlessHtmlPlugin(root: string): Plugin { + const rewrite = ( + req: { url?: string }, + _res: unknown, + next: () => void, + ): void => { + const url = req.url || '' + const [pathname, query] = url.split('?') + if ( + pathname && + pathname !== '/' && + !pathname.endsWith('/') && + !/\.[^/]+$/.test(pathname) && + fs.existsSync(path.join(root, pathname + '.html')) + ) { + req.url = pathname + '.html' + (query ? '?' + query : '') + } + next() + } + return { + name: 'extensionless-html', + configureServer(server) { + server.middlewares.use(rewrite) + }, + configurePreviewServer(server) { + server.middlewares.use(rewrite) + }, + } +} + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), '') + const skillAgentTarget = env.VITE_SKILL_AGENT_URL || 'http://127.0.0.1:8100' + const teamApiTarget = env.VITE_TEAM_API_URL || 'https://laimiaoai.com' + const skillAgentProxy = { + '/skill-agent': { + target: skillAgentTarget, + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/skill-agent/, ''), + }, + } + const teamApiProxy = { + '/api': { + target: teamApiTarget, + changeOrigin: true, + }, + } + + return { + plugins: [extensionlessHtmlPlugin(process.cwd()), vue(), tailwindcss()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + server: { + port: 5181, + proxy: { ...skillAgentProxy, ...teamApiProxy }, + }, + preview: { + proxy: { ...skillAgentProxy, ...teamApiProxy }, + }, + build: { + rollupOptions: { + input: { + index: 'index.html', + app: 'app.html', + generate: 'generate.html', + me: 'me.html', + login: 'login/index.html', + invite: 'invite/index.html', + library: 'library.html', + }, + }, + }, + } +})