commit 1b603efa0c8c347269309ef1940300d95d425f35 Author: liuyonghe0111 <1763195287@qq.com> Date: Thu Nov 13 16:38:25 2025 +0800 feat: 初始化仓库 diff --git a/.cursor/rules/project.mdc b/.cursor/rules/project.mdc new file mode 100644 index 0000000..1c607df --- /dev/null +++ b/.cursor/rules/project.mdc @@ -0,0 +1,106 @@ +--- +alwaysApply: false +--- +You are an expert in Typescript, React, Node.js, Next.js App Router, Shadcn UI, Radix UI, Tailwind. + + Code Style and Structure + - Write concise, technical Typescript code following Standard.js rules. + - Use functional and declarative programming patterns; avoid classes. + - Prefer iteration and modularization over code duplication. + - Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError). + - Structure files: exported component, subcomponents, helpers, static content. + + Standard.js Rules + - Use 2 space indentation. + - Use double quotes for strings except to avoid escaping. + - No semicolons (unless required to disambiguate statements). + - No unused variables. + - Add a space after keywords. + - Add a space before a function declaration's parentheses. + - Always use === instead of ==. + - Infix operators must be spaced. + - Commas should have a space after them. + - Keep else statements on the same line as their curly braces. + - For multi-line if statements, use curly braces. + - Always handle the err function parameter. + - Use camelcase for variables and functions. + - Use PascalCase for constructors and React components. + + Component Architecture + - Use class-variance-authority (cva) for variant management + - Implement compound components when appropriate + - Export individual subcomponents for flexibility + - Use React.forwardRef for ref forwarding + - Implement proper TypeScript interfaces + - Document props and examples + + React Best Practices + - Use functional components with prop-types for type checking. + - Use the "function" keyword for component definitions. + - Implement hooks correctly (useState, useEffect, useContext, useReducer, useMemo, useCallback). + - Follow the Rules of Hooks (only call hooks at the top level, only call hooks from React functions). + - Create custom hooks to extract reusable component logic. + - Use React.memo() for component memoization when appropriate. + - Implement useCallback for memoizing functions passed as props. + - Use useMemo for expensive computations. + - Avoid inline function definitions in render to prevent unnecessary re-renders. + - Prefer composition over inheritance. + - Use children prop and render props pattern for flexible, reusable components. + - Implement React.lazy() and Suspense for code splitting. + - Use refs sparingly and mainly for DOM access. + - Prefer controlled components over uncontrolled components. + - Implement error boundaries to catch and handle errors gracefully. + - Use cleanup functions in useEffect to prevent memory leaks. + - Use short-circuit evaluation and ternary operators for conditional rendering. + + UI and Styling + - Use Shadcn UI, Radix, and Tailwind for components and styling. + - The new version of tailwindcss no longer supports tailwind.config.ts, but defines the token directly in the css. + - Using design tokens from css/tailwindcss.css. + - Using tokens the right way with tailwind. + - Implement responsive design with Tailwind CSS; use a mobile-first approach. + + Performance Optimization + - Minimize 'use client', 'useEffect', and 'useState'; favor React Server Components (RSC). + - Wrap client components in Suspense with fallback. + - Use dynamic loading for non-critical components. + - Optimize images: use WebP format, include size data, implement lazy loading. + - Implement route-based code splitting in Next.js. + - Minimize the use of global styles; prefer modular, scoped styles. + - Use PurgeCSS with Tailwind to remove unused styles in production. + + Forms and Validation + - Use controlled components for form inputs. + - Implement form validation (client-side and server-side). + - Consider using libraries like react-hook-form for complex forms. + - Use Zod or Joi for schema validation. + + Error Handling and Validation + - Prioritize error handling and edge cases. + - Handle errors and edge cases at the beginning of functions. + - Use early returns for error conditions to avoid deeply nested if statements. + - Place the happy path last in the function for improved readability. + - Avoid unnecessary else statements; use if-return pattern instead. + - Use guard clauses to handle preconditions and invalid states early. + - Implement proper error logging and user-friendly error messages. + - Model expected errors as return values in Server Actions. + + Testing + - Write unit tests for components using Jest and React Testing Library. + - Implement integration tests for critical user flows. + - Use snapshot testing judiciously. + + Security + - Sanitize user inputs to prevent XSS attacks. + - Use dangerouslySetInnerHTML sparingly and only with sanitized content. + + Key Conventions + - Use 'nuqs' for URL search parameter state management. + - Optimize Web Vitals (LCP, CLS, FID). + - Limit 'use client': + - Favor server components and Next.js SSR. + - Use only for Web API access in small components. + - Avoid for data fetching or state management. + + Follow Next.js docs for Data Fetching, Rendering, and Routing. + - Avoid for data fetching or state management. \ No newline at end of file diff --git a/.cursor/rules/tailwind.mdc b/.cursor/rules/tailwind.mdc new file mode 100644 index 0000000..2f76f4a --- /dev/null +++ b/.cursor/rules/tailwind.mdc @@ -0,0 +1,161 @@ +--- +alwaysApply: false +--- +## Tailwind CSS 规则 +这个文件定义了项目中使用的 Tailwind CSS 变量和工具类规则。 + +相关文件: +- 主样式文件:`src/css/tailwindcss.css` - 包含所有 Tailwind 变量和工具类定义 + +### 渐变色工具类 + +以下是可用的渐变色工具类: + +- `bg-primary-gradient-normal`: 主色渐变(正常状态) + - 从 magenta-30 到 purple-40 的左到右渐变 +- `bg-primary-gradient-hover`: 主色渐变(悬停状态) + - 从 magenta-20 到 purple-30 的左到右渐变 +- `bg-primary-gradient-press`: 主色渐变(按下状态) + - 从 magenta-40 到 purple-50 的左到右渐变 +- `bg-primary-gradient-disabled`: 主色渐变(禁用状态) + +- `bg-context-subscribe-normal`: 订阅按钮渐变(正常状态) + - 从 purple-50 到 violet-50 的左到右渐变 +- `bg-context-subscribe-hover`: 订阅按钮渐变(悬停状态) + - 从 purple-30 到 violet-30 的左到右渐变 +- `bg-context-subscribe-press`: 订阅按钮渐变(按下状态) + - 从 purple-70 到 violet-70 的左到右渐变 + +- `bg-context-vip-normal`: VIP 渐变 + - 从 sky-20(0%) 到 violet-40(60%) 到 purple-30(100%) 的左到右渐变 + +- `bg-context-recharge-normal`: 充值按钮渐变 + - 从 yellow-0 到 yellow-70 的左到右渐变 + +### 字体工具类 + +Display 字体系列: +- `txt-display-l`: + - 字体:Oleo Script Swash Caps + - 大小:64px + - 行高:80px + - 字重:Regular (400) + +- `txt-display-m`: + - 字体:Oleo Script Swash Caps + - 大小:48px + - 行高:56px + - 字重:Regular (400) + +- `txt-display-s`: + - 字体:Oleo Script Swash Caps + - 大小:24px + - 行高:28px + - 字重:Regular (400) + +标题字体系列: +- `txt-headline-l`: + - 字体:Poppins + - 大小:48px + - 行高:56px + - 字重:Bold (700) + +- `txt-headline-m`: + - 字体:Poppins + - 大小:36px + - 行高:48px + - 字重:Bold (700) + +正文字体系列: +- `txt-body-l`: + - 字体:Poppins + - 大小:16px + - 行高:24px + - 字重:Regular (400) + +- `txt-body-m`: + - 字体:Poppins + - 大小:14px + - 行高:20px + - 字重:Regular (400) + +- `txt-body-s`: + - 字体:Poppins + - 大小:12px + - 行高:20px + - 字重:Regular (400) + +标签字体系列: +- `txt-label-l`: + - 字体:Poppins + - 大小:16px + - 行高:24px + - 字重:Medium (500) + +- `txt-label-m`: + - 字体:Poppins + - 大小:14px + - 行高:20px + - 字重:Medium (500) + +- `txt-label-s`: + - 字体:Poppins + - 大小:12px + - 行高:20px + - 字重:Medium (500) + +数字显示字体系列: +- `txt-numDisplay-xl`: + - 字体:Display Number Font + - 大小:64px + - 行高:80px + - 字重:Bold (700) + +- `txt-numDisplay-l`: + - 字体:Display Number Font + - 大小:48px + - 行高:56px + - 字重:Bold (700) + +等宽数字字体系列: +- `txt-numMonotype-xl`: + - 字体:Poppins + - 大小:24px + - 行高:28px + - 字重:Bold (700) + +- `txt-numMonotype-l`: + - 字体:Poppins + - 大小:20px + - 行高:24px + - 字重:Bold (700) + +### 使用说明 + +1. 渐变色使用: +```tsx +
+ // 内容 +
+``` + +2. 字体使用: +```tsx +

大标题

+

正文内容

+12345 +``` + +3. 响应式设计: +所有工具类都支持响应式前缀: +- sm: ≥768px +- md: ≥1024px +- lg: ≥1280px +- xl: ≥1536px + +例如: +```tsx +

+ 响应式标题 +

+``` diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..bcdf512 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,31 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- `src/app` hosts the App Router; use `(auth)` and `(main)` route groups and keep route-only components inside their segment folders. +- `src/components` holds shared UI; keep primitives in `components/ui` and group feature widgets under clear folder names. +- `src/lib`, `src/services`, and `src/utils` house shared logic, API clients, and helpers; extend an existing module before adding a new directory. +- Mock handlers live in `src/mocks`, MSW’s worker sits in `public/mockServiceWorker.js`, localization bundles under `public/locales`, and generated docs go to `docs/`. + +## Build, Test, and Development Commands +- `npm run dev` starts the Turbopack dev server with MSW auto-registration when `NEXT_PUBLIC_ENABLE_MOCK=true`. +- `npm run build` compiles the production bundle; run after routing or configuration changes. +- `npm run start` serves the built app and mirrors production runtime. +- `npm run lint` applies the Next.js ESLint preset; resolve warnings before review. +- `npm run i18n:scan`, `npm run i18n:scan-custom`, and `npm run i18n:convert` refresh translation keys and write `docs/copy-audit.xlsx`. + +## Coding Style & Naming Conventions +- TypeScript is required; keep strict types at module boundaries and define payload interfaces explicitly. +- Follow the house formatting: two-space indentation, double quotes, no semicolons, and Tailwind classes composed with `cn`. +- Use PascalCase for React components, camelCase for utilities, `use` prefix for hooks, and kebab-case file names in routes. +- Reuse theme tokens and shared icons through design-system helpers; avoid ad-hoc color values or inline SVG copies. + +## Testing Guidelines +- There is no global Jest/Vitest runner; smoke tests such as `src/utils/textParser.test.ts` execute with `npx tsx `—mirror that pattern for quick unit checks. +- Keep exploratory scripts or `.test.ts` files beside the code they exercise and strip console noise before shipping. +- Prioritize integration checks through the dev server plus MSW, and document manual test steps in the PR when automation is absent. + +## Commit & Pull Request Guidelines +- Commit subjects are present-tense, sentence-style summaries (see `git log`); add rationale in the body when touching multiple areas. +- Scope each branch to a vertical slice (`feature/`, `fix/`, or `chore/`) and rebase on `main` before review. +- PRs need a concise summary, screenshots for UI updates, environment-variable callouts, and links to related issues or docs. +- Flag data, security, or localization assumptions so reviewers can validate configuration changes quickly. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a3ed0c --- /dev/null +++ b/README.md @@ -0,0 +1,207 @@ +# Crushlevel Next.js Application + +这是一个基于 Next.js 的现代化Web应用,支持多种社交登录方式。 + +## 功能特性 + +- 🎮 **多平台社交登录**: 支持Discord、Google、Apple登录 +- 🔐 **完整认证流程**: OAuth2.0认证,JWT token管理 +- 📱 **设备管理**: 自动设备ID生成和管理 +- 🎭 **开发环境Mock**: 使用MSW进行API模拟 +- 🛡️ **中间件保护**: 路由级别的认证保护 + +## 快速开始 + +### 1. 安装依赖 + +```bash +npm install +``` + +### 2. 环境变量配置 + +复制 `.env.local.example` 文件为 `.env.local` 并配置相应的环境变量: + +```bash +cp .env.local.example .env.local +``` + +#### Discord OAuth 配置 + +要启用Discord登录,你需要在Discord开发者平台创建应用: + +1. 访问 [Discord Developer Portal](https://discord.com/developers/applications) +2. 点击 "New Application" 创建新应用 +3. 在应用设置页面: + - 复制 **Application ID** 作为 `NEXT_PUBLIC_DISCORD_CLIENT_ID` + - 在 "OAuth2" 标签页生成 **Client Secret** 作为 `DISCORD_CLIENT_SECRET` + - 添加回调URL: `http://localhost:3000/api/auth/discord/callback` (开发环境) + - 选择 scopes: `identify`, `email` + +```env +# Discord OAuth 配置 +NEXT_PUBLIC_DISCORD_CLIENT_ID=your_discord_client_id_here +DISCORD_CLIENT_SECRET=your_discord_client_secret_here + +# 应用URL(生产环境需要修改) +NEXT_PUBLIC_APP_URL=http://localhost:3000 + +# 开发环境Mock配置 +NEXT_PUBLIC_ENABLE_MOCK=true +``` + +#### 其他OAuth配置(可选) + +```env +# Google OAuth(未来支持) +NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id_here +GOOGLE_CLIENT_SECRET=your_google_client_secret_here + +# Apple OAuth(未来支持) +NEXT_PUBLIC_APPLE_CLIENT_ID=your_apple_client_id_here +APPLE_CLIENT_SECRET=your_apple_client_secret_here +``` + +### 3. 启动开发服务器 + +```bash +npm run dev +``` + +应用将在 http://localhost:3000 启动。 + +## Discord登录流程 + +### 1. 用户点击Discord登录按钮 +- 系统生成随机state参数用于安全验证 +- 跳转到Discord授权页面 + +### 2. Discord授权 +用户在Discord页面授权后,Discord重定向到回调URL并携带授权码 + +### 3. 回调处理 +- API路由 `/api/auth/discord/callback` 接收授权码 +- 将授权码作为URL参数重定向到前端登录页面 + +### 4. 前端登录处理 +- 前端登录页面检测到URL中的`discord_code`参数 +- 使用授权码调用后端API: `POST /web/third/login` +- 后端验证授权码并返回认证token + +### 5. 登录完成 +- 前端保存token并重定向到首页 +- 完成整个登录流程 + +## 开发环境Mock + +项目使用MSW进行API模拟,在开发环境中: + +- 设置 `NEXT_PUBLIC_ENABLE_MOCK=true` 启用Mock +- 所有认证API请求都会被MSW拦截并返回模拟数据 +- 支持Discord、Google、Apple等第三方登录的模拟 + +### Mock功能特性 + +- ✅ 设备ID验证 +- ✅ 第三方登录模拟 +- ✅ Token管理 +- ✅ 用户信息管理 +- ✅ 错误场景模拟 + +## 项目结构 + +``` +src/ +├── app/ # Next.js App Router +│ ├── (auth)/ # 认证相关页面 +│ ├── (main)/ # 主应用页面 +│ └── api/ # API路由 +├── components/ # 可复用组件 +├── lib/ # 工具库 +│ ├── auth/ # 认证管理 +│ ├── http/ # HTTP客户端 +│ └── oauth/ # OAuth服务 +├── services/ # 业务服务 +├── mocks/ # MSW Mock配置 +└── types/ # TypeScript类型定义 +``` + +## API文档 + +### 认证相关API + +#### 第三方登录 +``` +POST /web/third/login +Content-Type: application/json + +{ + "appClient": "WEB", + "deviceCode": "设备唯一码", + "thirdToken": "第三方授权码", + "thirdType": "DISCORD" | "GOOGLE" | "APPLE" +} +``` + +#### 获取用户信息 +``` +GET /web/user/base-info +Headers: + AUTH_TK: "认证token" + AUTH_DID: "设备ID" +``` + +#### 登出 +``` +POST /web/user/logout +Headers: + AUTH_TK: "认证token" + AUTH_DID: "设备ID" +``` + +## 贡献指南 + +1. Fork本项目 +2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送分支 (`git push origin feature/AmazingFeature`) +5. 创建Pull Request + +## 许可证 + +本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。 + +## 文案清单导出(一次性盘点) + +为便于产品/运营统一校对当前所有展示文案,项目提供静态扫描脚本,自动抽取源码中的用户可见与可感知文案并导出为 Excel。 + +### 覆盖范围 +- JSX 文本节点与按钮/链接文案 +- 属性文案:`placeholder` / `title` / `alt` / `aria-*` / `label` +- 交互文案:`toast.*` / `message.*` / `alert` / `confirm` / `Dialog`/`Tooltip` 等常见调用 +- 表单校验与错误提示:`form.setError(..., { message })`、校验链条中的 `{ message: '...' }` + +### 运行 +```bash +# 生成 docs/copy-audit.xlsx +npx ts-node scripts/extract-copy.ts # 若 ESM 运行报错,请改用下行 +node scripts/extract-copy.cjs +``` + +输出文件:`docs/copy-audit.xlsx` + +### Excel 字段说明(Sheet: copy) +- `route`: Next.js App Router 路由(如 `(main)/home`)或 `shared` +- `file`: 文案所在文件(相对仓库根路径) +- `componentOrFn`: 组件或函数名(无法解析时为文件名) +- `kind`: 文案类型(`text` | `placeholder` | `title` | `alt` | `aria` | `label` | `toast` | `dialog` | `error` | `validation`) +- `keyOrLocator`: 定位信息(如 `Button.placeholder`、`toast.success`) +- `text`: 实际文案内容 +- `line`: 文案起始行号(近似定位) +- `count`: 在同一路由下相同文案出现次数(已聚合) +- `notes`: 预留备注 + +### 说明与边界 +- 仅提取可静态分析到的硬编码字符串;运行时拼接(仅变量)无法还原将被忽略 +- 会过滤明显的“代码样”字符串(如过长的标识符) +- 扫描目录为 `src/`,忽略 `node_modules/.next/__tests__/mocks` 等 diff --git a/components.json b/components.json new file mode 100644 index 0000000..ffe928f --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/docs/AiReplySuggestions.md b/docs/AiReplySuggestions.md new file mode 100644 index 0000000..3bf5f27 --- /dev/null +++ b/docs/AiReplySuggestions.md @@ -0,0 +1,92 @@ +# AI建议回复功能实现 + +## 功能概述 + +根据Figma设计稿实现了AI建议回复功能,用户可以在聊天界面中获取AI生成的回复建议,提高聊天效率。 + +## 实现组件 + +### 1. AiReplySuggestions.tsx +主要的AI建议回复组件,包含以下功能: +- 显示多个AI建议选项 +- 支持编辑建议内容 +- VIP解锁更多功能入口 +- 分页导航控制 + +### 2. useAiReplySuggestions.ts +状态管理Hook,处理: +- AI建议的获取和管理 +- 分页逻辑 +- 面板显示/隐藏控制 +- **自动刷新机制**:当面板打开时收到新AI消息会自动刷新建议 + +### 3. ChatInput.tsx(更新) +集成AI建议功能到聊天输入组件: +- 添加提示词按钮来触发建议面板 +- 处理建议选择和应用 +- 管理面板状态 + +## 设计细节 + +### 视觉设计 +- 遵循Figma设计稿的视觉样式 +- 使用毛玻璃效果和圆角设计 +- 渐变色彩搭配 +- 响应式布局 + +### 交互设计 +- 点击提示词按钮显示/隐藏建议面板 +- **点击建议卡片:直接发送该建议作为消息** +- **点击编辑图标:将建议文案放入输入框进行编辑** +- 分页控制支持浏览更多建议 +- VIP入口引导用户升级 + +## 使用方法 + +1. 在聊天界面点击输入框右侧的提示词按钮 +2. 查看AI生成的回复建议 +3. **直接点击建议卡片可立即发送该消息** +4. **点击编辑图标将建议放入输入框进行修改** +5. 使用分页控制查看更多建议选项 + +## 技术特点 + +- TypeScript类型安全 +- React Hooks状态管理 +- 响应式设计 +- 模块化组件结构 +- 可扩展的API接口设计 + +## 核心逻辑 + +### 建议获取时机 +- 只有当最后一条消息来自AI(对方)时,才会在打开面板时获取新建议 +- 如果最后一条消息来自用户,则显示之前缓存的建议或骨架屏 +- 每次检测到新的AI消息后,第一次打开面板会重新获取建议 + +### 骨架屏显示 +- **骨架屏已集成到建议弹窗内部**,不再是独立组件 +- 在API调用期间显示骨架屏,提升用户体验 +- 骨架屏固定显示2条建议的布局结构 + +### 分页机制 +- **API一次性返回所有建议数据**,不是分页请求 +- 每页显示2条建议 +- 根据API返回的总数自动计算页数 +- **点击左右切换只是前端切换显示,不会重新请求接口** + +### 缓存策略 +- 建议会被缓存,避免重复API调用 +- 只有检测到新的AI消息时才会清空缓存重新获取 +- **自动刷新**:当面板已打开且收到新AI消息时,自动刷新建议 + +## API集成 + +已集成真实的AI建议API(`genSupContentV2`),替换了之前的模拟数据。 + +## 后续扩展 + +- 添加建议个性化定制 +- 支持更多建议类型 +- 添加使用统计和优化 +- 优化缓存策略和错误处理 diff --git a/docs/AvatarSetting.md b/docs/AvatarSetting.md new file mode 100644 index 0000000..b92fa0a --- /dev/null +++ b/docs/AvatarSetting.md @@ -0,0 +1,103 @@ +# AvatarSetting 组件 + +这是一个根据Figma设计稿实现的头像设置模态框组件,支持头像预览、编辑、上传和删除功能。 + +## 功能特性 + +- 🖼️ 大尺寸圆形头像预览 +- ✂️ 头像裁剪功能 +- 📤 文件上传支持 +- 🗑️ 头像删除功能 +- 📱 响应式设计 +- 🎨 符合设计规范的UI +- 🔄 模态框形式,覆盖在页面上 + +## 使用方法 + +```tsx +import AvatarSetting from "@/app/(main)/profile/components/AvatarSetting" + +function ProfilePage() { + const [isAvatarSettingOpen, setIsAvatarSettingOpen] = useState(false) + const [currentAvatar, setCurrentAvatar] = useState("") + + const handleAvatarChange = (avatarUrl: string) => { + setCurrentAvatar(avatarUrl) + // 这里可以调用API保存头像 + } + + const handleAvatarDelete = () => { + setCurrentAvatar("") + // 这里可以调用API删除头像 + } + + const openAvatarSetting = () => { + setIsAvatarSettingOpen(true) + } + + const closeAvatarSetting = () => { + setIsAvatarSettingOpen(false) + } + + return ( +
+ {/* 触发按钮 */} + + + {/* 头像设置模态框 */} + +
+ ) +} +``` + +## Props + +| 属性 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| `isOpen` | `boolean` | `false` | 控制模态框显示/隐藏 | +| `onClose` | `() => void` | `undefined` | 关闭模态框的回调 | +| `currentAvatar` | `string \| undefined` | `undefined` | 当前头像URL | +| `onAvatarChange` | `(avatarUrl: string) => void` | `undefined` | 头像变更回调 | +| `onAvatarDelete` | `() => void` | `undefined` | 头像删除回调 | +| `className` | `string` | `undefined` | 自定义CSS类名 | + +## 层级关系 + +- 头像设置模态框:`z-40` +- 头像裁剪弹窗:`z-50`(更高层级) + +## 文件格式支持 + +- 支持的格式:JPG、JPEG、PNG +- 文件大小限制:5MB +- VIP用户支持GIF格式 + +## 设计规范 + +- 背景色:`#211a2b` +- 头像尺寸:512x512px +- 按钮渐变:从 `#f264a4` 到 `#c241e6` +- 圆角:999px(完全圆形) +- 模态框尺寸:800x800px + +## 测试页面 + +访问 `/test-avatar-setting` 可以查看组件的演示效果。 + +## 注意事项 + +1. 组件现在是模态框形式,会覆盖在页面上 +2. 使用现有的 `AvatarCropModal` 组件进行头像裁剪 +3. 文件上传使用原生的 `input[type="file"]` 实现 +4. 组件会自动验证文件类型和大小 +5. 裁剪后的图片会自动转换为圆形 +6. 裁剪弹窗的层级比头像设置模态框更高 \ No newline at end of file diff --git a/docs/CrushLevelAvatar.md b/docs/CrushLevelAvatar.md new file mode 100644 index 0000000..a153c2e --- /dev/null +++ b/docs/CrushLevelAvatar.md @@ -0,0 +1,170 @@ +# 心动等级头像组件 (CrushLevelAvatar) + +## 概述 + +`CrushLevelAvatar` 是一个展示用户与AI角色心动等级的头像组件,根据设计稿实现了双头像重叠布局、心动等级徽章显示以及动画效果。 + +## 功能特性 + +### 1. 状态处理 +- **无等级状态**:只显示AI头像和昵称 +- **有等级状态**:显示AI和用户双头像,包含心动等级信息 + +### 2. 视觉设计 +- **双头像布局**:AI头像和用户头像并排显示,带有白色边框和阴影 +- **多层背景装饰**:三层渐变圆形背景,从外到内颜色递进 +- **心动等级徽章**:居中显示的心形徽章,包含等级数字 +- **角色信息展示**:角色名称和心动温度标签 + +### 3. 动画效果 +- **等级变化动画**:心形背景从大到小消失,数字等级渐变切换 +- **分层延迟**:三层心形背景依次消失(0ms, 100ms, 200ms延迟) +- **数字切换**:背景完全消失后,等级数字淡出淡入切换到新等级 + +## 组件属性 + +```typescript +interface CrushLevelAvatarProps { + size?: "large" | "small"; // 头像尺寸 + showAnimation?: boolean; // 是否显示等级变化动画 +} +``` + +## 使用示例 + +```tsx +import CrushLevelAvatar from './components/CrushLevelAvatar'; + +// 基础使用 + + +// 大尺寸带动画 + +``` + +## 依赖要求 + +组件需要在以下上下文中使用: + +1. **ChatConfigContext** - 提供AI信息和ID +2. **用户认证** - 获取当前用户信息 +3. **IM服务** - 获取心动等级数据 + +## 数据来源 + +- `useChatConfig()` - AI信息和聊天配置 +- `useCurrentUser()` - 当前用户信息 +- `useGetHeartbeatLevel()` - 心动等级数据 +- `useHeartLevelTextFromLevel()` - 等级文本转换 + +## 样式系统 + +### CSS 动画类 +- `.animate-scale-down` - 缩放动画 +- `.animate-delay-100/200/300` - 动画延迟 + +### 颜色设计 +- 外层背景:`from-purple-500/20` +- 中层背景:`from-pink-500/30` +- 内层背景:`from-magenta-500/40` +- 心形徽章:`from-pink-400 to-red-500` + +## 实现细节 + +### 背景装饰层 +```tsx +{/* 心形背景层 - 使用SVG图标 */} +
+ heart background +
+``` + +### 心动等级徽章 +```tsx +{/* 心形背景 + 等级数字 */} +
+ heart +
+ {displayLevel?.replace('LEVEL_', '') || '1'} +
+
+``` + +### 动画触发机制 +```tsx +// 监听等级变化触发动画 +useEffect(() => { + if (showAnimation && heartbeatLevel && heartbeatLevel !== displayLevel) { + setIsLevelChanging(true); + setAnimationKey(prev => prev + 1); + + // 背景消失后更新等级数字 + setTimeout(() => { + setDisplayLevel(heartbeatLevel); + setIsLevelChanging(false); + }, 600); // 背景消失动画时长 + } +}, [heartbeatLevel, showAnimation, displayLevel]); +``` + +### CSS 动画定义 +```css +/* 心形背景消失动画 */ +@keyframes scale-fade-out { + 0% { transform: scale(1); opacity: 1; } + 100% { transform: scale(0.3); opacity: 0; } +} + +/* 等级数字变化动画 */ +@keyframes level-change { + 0% { opacity: 1; transform: scale(1); } + 50% { opacity: 0; transform: scale(0.8); } + 100% { opacity: 1; transform: scale(1); } +} +``` + +## 注意事项 + +1. **上下文依赖**:组件必须在正确的 Context 环境中使用 +2. **性能考虑**:动画效果会增加渲染开销 +3. **响应式设计**:组件在不同屏幕尺寸下的表现 +4. **数据安全**:处理用户头像加载失败的情况 +5. **图标依赖**:需要确保 `/icons/crushlevel_heart.svg` 文件存在 + +## 测试 + +可以通过访问 `/test-crush-level-avatar` 页面查看组件的不同状态和配置效果。 + +### 调试功能 +在浏览器控制台中可以调用以下函数来手动触发动画: +```javascript +window.triggerLevelAnimation(); // 触发等级变化动画 +``` + +## 更新历史 + +- 2024-01-XX:初始实现,包含基础功能 +- 2024-01-XX:添加动画效果和背景装饰 +- 2024-01-XX:优化性能和用户体验 diff --git a/docs/DesignTokens.md b/docs/DesignTokens.md new file mode 100644 index 0000000..7409ef7 --- /dev/null +++ b/docs/DesignTokens.md @@ -0,0 +1,404 @@ +# Design Tokens for Website + +This document outlines the design tokens defined in the `Tokens.xlsx` file, including global tokens (base values) and web system tokens (specific to Tailwind CSS integration). The tokens are organized into two sections corresponding to the Excel sheets: `Global tokens` and `Web sys tokens`. + +## Global Tokens + +The `Global tokens` sheet defines foundational design tokens, including colors, transparency, angles, typography, ratios, radii, borders, spacing, and breakpoints. These serve as the base values referenced by other tokens. + +| Type | Token | Value | 移动端约定值 | 有调整会标黄 | 新增会标蓝 | 可不录入的文字会变红 | +|-------------|--------------------------------|----------------------------------------|--------------|--------------|------------|----------------------| +| color | glo.color.orange.0 | #FFECDE | | | | | +| | glo.color.orange.10 | #FFD7B8 | | | | | +| | glo.color.orange.20 | #FFBF8F | | | | | +| | glo.color.orange.30 | #FFA264 | | | | | +| | glo.color.orange.40 | #FD8239 | | | | | +| | glo.color.orange.50 | #F25E0F | | | | | +| | glo.color.orange.60 | #D04500 | | | | | +| | glo.color.orange.70 | #A83400 | | | | | +| | glo.color.orange.80 | #7B2300 | | | | | +| | glo.color.orange.90 | #4D1400 | | | | | +| | glo.color.yellow.0 | #FFF8DE | | | | | +| | glo.color.yellow.10 | #FFEFB3 | | | | | +| | glo.color.yellow.20 | #FFE386 | | | | | +| | glo.color.yellow.30 | #FCD258 | | | | | +| | glo.color.yellow.40 | #F3BC2A | | | | | +| | glo.color.yellow.50 | #E6A100 | | | | | +| | glo.color.yellow.60 | #C78800 | | | | | +| | glo.color.yellow.70 | #A26B00 | | | | | +| | glo.color.yellow.80 | #784D00 | | | | | +| | glo.color.yellow.90 | #4D2F00 | | | | | +| | glo.color.grass.0 | #F8FFDE | | | | | +| | glo.color.grass.10 | #EDFCB8 | | | | | +| | glo.color.grass.20 | #E0F68F | | | | | +| | glo.color.grass.30 | #CFED67 | | | | | +| | glo.color.grass.40 | #BAE041 | | | | | +| | glo.color.grass.50 | #A0CD1E | | | | | +| | glo.color.grass.60 | #82B500 | | | | | +| | glo.color.grass.70 | #689600 | | | | | +| | glo.color.grass.80 | #4B7200 | | | | | +| | glo.color.grass.90 | #304D00 | | | | | +| | glo.color.green.0 | #DEFFE7 | | | | | +| | glo.color.green.10 | #B9FCCD | | | | | +| | glo.color.green.20 | #94F7B1 | | | | | +| | glo.color.green.30 | #6FEE96 | | | | | +| | glo.color.green.40 | #4AE27B | | | | | +| | glo.color.green.50 | #28D061 | | | | | +| | glo.color.green.60 | #0BB84A | | | | | +| | glo.color.green.70 | #00983C | | | | | +| | glo.color.green.80 | #007331 | | | | | +| | glo.color.green.90 | #004D22 | | | | | +| | glo.color.mint.0 | #DEFFF8 | | | | | +| | glo.color.mint.10 | #B6FBED | | | | | +| | glo.color.mint.20 | #8DF3E2 | | | | | +| | glo.color.mint.30 | #65E9D5 | | | | | +| | glo.color.mint.40 | #3FDAC4 | | | | | +| | glo.color.mint.50 | #1DC7B0 | | | | | +| | glo.color.mint.60 | #00AD96 | | | | | +| | glo.color.mint.70 | #009182 | | | | | +| | glo.color.mint.80 | #006F67 | | | | | +| | glo.color.mint.90 | #004D49 | | | | | +| | glo.color.sky.0 | #DEECFF | | | | | +| | glo.color.sky.10 | #B5D2FD | | | | | +| | glo.color.sky.20 | #8CB5F9 | | | | | +| | glo.color.sky.30 | #6296F2 | | | | | +| | glo.color.sky.40 | #3A76E6 | | | | | +| | glo.color.sky.50 | #1E58D2 | | | | | +| | glo.color.sky.60 | #063BB8 | | | | | +| | glo.color.sky.70 | #002A98 | | | | | +| | glo.color.sky.80 | #001E73 | | | | | +| | glo.color.sky.90 | #00134D | | | | | +| | glo.color.blue.0 | #DEE0FF | | | | | +| | glo.color.blue.10 | #BCBEFF | | | | | +| | glo.color.blue.20 | #9797FF | | | | | +| | glo.color.blue.30 | #7370FF | | | | | +| | glo.color.blue.40 | #4E48FF | | | | | +| | glo.color.blue.50 | #3126E6 | | | | | +| | glo.color.blue.60 | #180AC7 | | | | | +| | glo.color.blue.70 | #0F00A2 | | | | | +| | glo.color.blue.80 | #0D0078 | | | | | +| | glo.color.blue.90 | #09004D | | | | | +| | glo.color.violet.0 | #E4DEFF | | | | | +| | glo.color.violet.10 | #C7B7FD | | | | | +| | glo.color.violet.20 | #AA90F9 | | | | | +| | glo.color.violet.30 | #8D68F2 | | | | | +| | glo.color.violet.40 | #7B47FF | | | | | +| | glo.color.violet.50 | #5923D2 | | | | | +| | glo.color.violet.60 | #4309B8 | | | | | +| | glo.color.violet.70 | #340098 | | | | | +| | glo.color.violet.80 | #290073 | | | | | +| | glo.color.violet.90 | #1C004D | | | | | +| | glo.color.purple.0 | #FBDEFF | | | | | +| | glo.color.purple.10 | #F2B7FD | | | | | +| | glo.color.purple.20 | #E690F9 | | | | | +| | glo.color.purple.30 | #D668F2 | | | | | +| | glo.color.purple.40 | #C241E6 | | | | | +| | glo.color.purple.50 | #A823D2 | | | | | +| | glo.color.purple.60 | #8A09B8 | | | | | +| | glo.color.purple.70 | #6E0098 | | | | | +| | glo.color.purple.80 | #520073 | | | | | +| | glo.color.purple.90 | #36004D | | | | | +| | glo.color.magenta.0 | #FBDEFF | | | | | +| | glo.color.magenta.10 | #FDB6D3 | | | | | +| | glo.color.magenta.20 | #F98DBC | | | | | +| | glo.color.magenta.30 | #F264A4 | | | | | +| | glo.color.magenta.40 | #E63C8B | | | | | +| | glo.color.magenta.50 | #D21F77 | | | | | +| | glo.color.magenta.60 | #B80761 | | | | | +| | glo.color.magenta.70 | #980050 | | | | | +| | glo.color.magenta.80 | #73003E | | | | | +| | glo.color.magenta.90 | #4D002A | | | | | +| | glo.color.red.0 | #FFDEDE | | | | | +| | glo.color.red.10 | #FFBCBC | | | | | +| | glo.color.red.20 | #FF9696 | | | | | +| | glo.color.red.30 | #F97372 | | | | | +| | glo.color.red.40 | #EF4E4D | | | | | +| | glo.color.red.50 | #E12A2A | | | | | +| | glo.color.red.60 | #C2110E | | | | | +| | glo.color.red.70 | #A00700 | | | | | +| | glo.color.red.80 | #770800 | | | | | +| | glo.color.red.90 | #4D0600 | | | | | +| | glo.color.grey.0 | #E8E4EB | | | | | +| | glo.color.grey.10 | #D4D0D8 | | | | | +| | glo.color.grey.20 | #AAA3B1 | | | | | +| | glo.color.grey.30 | #958E9E | | | | | +| | glo.color.grey.40 | #847D8B | | | | | +| | glo.color.grey.50 | #706A78 | | | | | +| | glo.color.grey.60 | #5C5565 | | | | | +| | glo.color.grey.70 | #484151 | | | | | +| | glo.color.grey.80 | #352E3E | | | | | +| | glo.color.grey.90 | #282233 | | | | | +| | glo.color.grey.100 | #211A2B | | | | | +| | glo.color.white | #FFFFFF | | | | | +| | glo.color.black | #000000 | | | | | +| Transparent | glo.transparent.t0 | 0 | | | | | +| | glo.transparent.t2 | 0.02 | | | | | +| | glo.transparent.t4 | 0.04 | | | | | +| | glo.transparent.t6 | 0.06 | | | | | +| | glo.transparent.t8 | 0.08 | | | | | +| | glo.transparent.t12 | 0.12 | | | | | +| | glo.transparent.t15 | 0.15 | | | | | +| | glo.transparent.t20 | 0.2 | | | | | +| | glo.transparent.t25 | 0.25 | | | | | +| | glo.transparent.t30 | 0.3 | | | | | +| | glo.transparent.t45 | 0.45 | | | | | +| | glo.transparent.t65 | 0.65 | | | | | +| | glo.transparent.t85 | 0.85 | | | | | +| Degree | glo.deg.ltr | to Right | | | | | +| | glo.deg.ttb | to Bottom | | | | | +| | glo.deg.lttrb | to Bottom Right | | | | | +| Typeface | glo.font.family.sys | Poppins | | | | | +| | glo.font.family.numDisplay | DIN Alternate | | | | | +| | glo.font.family.num | Poppins | | | | | +| | glo.font.family.display | Oleo Script Swash Caps | | | | | +| | glo.font.style.italic | Italic | | | | | +| | glo.font.size.64 | 64 | | | | | +| | glo.font.size.48 | 48 | | | | | +| | glo.font.size.36 | 36 | | | | | +| | glo.font.size.24 | 24 | | | | | +| | glo.font.size.20 | 20 | | | | | +| | glo.font.size.18 | 18 | | | | | +| | glo.font.size.16 | 16 | | | | | +| | glo.font.size.14 | 14 | | | | | +| | glo.font.size.12 | 12 | | | | | +| | glo.font.weight.regular | 400 | | | | | +| | glo.font.weight.medium | 500 | | | | | +| | glo.font.weight.semibold | 600 | | | | | +| | glo.font.weight.bold | 700 | | | | | +| | glo.font.lineheight.size64 | 80px | | | | | +| | glo.font.lineheight.size48 | 56px | | | | | +| | glo.font.lineheight.size36 | 48px | | | | | +| | glo.font.lineheight.size24 | 28px | | | | | +| | glo.font.lineheight.size20 | 24px | | | | | +| | glo.font.lineheight.size18 | 24px | | | | | +| | glo.font.lineheight.size16 | 24px | | | | | +| | glo.font.lineheight.size14 | 20px | | | | | +| | glo.font.lineheight.size12 | 20px | | | | | +| | glo.font.lineheight.size0 | 1 | | | | | +| radio | glo.radio.1.1 | 1:1 | | | | | +| | glo.radio.4.3 | 4:3 | | | | | +| | glo.radio.3.2 | 3:2 | | | | | +| | glo.radio.2.1 | 2:1 | | | | | +| | glo.radio.16.9 | 16:9 | | | | | +| radius | glo.radius.4 | 4 | | | | | +| | glo.radius.8 | 8 | | | | | +| | glo.radius.12 | 12 | | | | | +| | glo.radius.16 | 16 | | | | | +| | glo.radius.24 | 24 | | | | | +| | glo.radius.round | 0.5 | | | | | +| border | glo.border.1 | 1px | | | | | +| | glo.border.2 | 2px | | | | | +| | glo.border.4 | 4px | | | | | +| space | glo.spacing.4 | 4px | | | | | +| | glo.spacing.8 | 8px | | | | | +| | glo.spacing.12 | 12px | | | | | +| | glo.spacing.16 | 16px | | | | | +| | glo.spacing.24 | 24px | | | | | +| | glo.spacing.32 | 32px | | | | | +| | glo.spacing.48 | 48px | | | | | +| | glo.spacing.64 | 64px | | | | | +| | glo.spacing.80 | 80px | | | | | +| | glo.spacing.128 | 128px | | | | | +| breackpoint | breackpoint.xs | <768px | | | | | +| | breackpoint.s | ≥768px | | | | | +| | breackpoint.m | ≥1024px | | | | | +| | breackpoint.l | ≥1280px | | | | | +| | breackpoint.xl | ≥1536 | | | | | + +## Web System Tokens + +The `Web sys tokens` sheet defines tokens specifically for web use, including colors, typography, shadows, radii, and borders. These are intended for integration with Tailwind CSS, particularly for color definitions in dark and light themes. + +| Type | | Token | Value@Dark Theme(Default) | Value on White | +|-----------------|---------------|------------------------------------------|-------------------------------------------------------------------------------------------|---------------------------------------------| +| color | primary | color.primary.normal | $glo.color.magenta.50 | | +| | | color.primary.hover | $glo.color.magenta.40 | | +| | | color.primary.press | $glo.color.magenta.60 | | +| | | color.primary.disabled | $color.surface.nest.disabled | | +| | | color.primary.variant.normal | $glo.color.magenta.40 | | +| | | color.primary.variant.hover | $glo.color.magenta.30 | | +| | | color.primary.variant.press | $glo.color.magenta.50 | | +| | | color.primary.variant.disabled | $color.surface.nest.disabled | | +| | | color.primary.gradient.normal | linear-gradient($glo.deg.ltr,$glo.color.magenta.30,$glo.color.purple.40) | | +| | | color.primary.gradient.hover | linear-gradient($glo.deg.ltr,$glo.color.magenta.20,$glo.color.purple.30) | | +| | | color.primary.gradient.press | linear-gradient($glo.deg.ltr,$glo.color.magenta.40,$glo.color.purple.50) | | +| | | color.primary.gradient.disabled | $color.surface.nest.disabled | | +| | | color.primary.onpic.normal | $glo.color.violet.40 | $glo.transparent.t85 | +| | | color.primary.onpic.hover | $glo.color.violet.30 | $glo.transparent.t85 | +| | | color.primary.onpic.press | $glo.color.violet.50 | $glo.transparent.t85 | +| | Important | color.important.normal | $glo.color.red.50 | | +| | | color.important.hover | $glo.color.red.40 | | +| | | color.important.press | $glo.color.red.60 | | +| | | color.important.disabled | $color.surface.nest.disabled | | +| | | color.important.variant.normal | $glo.color.red.40 | | +| | | color.important.variant.hover | $glo.color.red.30 | | +| | | color.important.variant.press | $glo.color.red.50 | | +| | | color.important.variant.disabled | $glo.color.blue.10 | $glo.transparent.t25 | +| | | color.important.gradient.normal | sha | | +| | | color.important.gradient.hover | linear-gradient($glo.deg.ltr,$glo.color.orange.40,$glo.color.red.40) | | +| | | color.important.gradient.press | linear-gradient($glo.deg.ltr,$glo.color.orange.60,$glo.color.red.60) | | +| | | color.important.gradient.disabled | $color.surface.nest.disabled | | +| | | color.important.onpic.normal | $glo.color.red.50 | $glo.transparent.t85 | +| | positive | color.positive.normal | $glo.color.mint.60 | | +| | | color.positive.hover | $glo.color.mint.50 | | +| | | color.positive.press | $glo.color.mint.70 | | +| | | color.positive.disabled | $color.surface.nest.disabled | | +| | | color.positive.variant.normal | $glo.color.mint.40 | | +| | | color.positive.variant.hover | $glo.color.mint.30 | | +| | | color.positive.variant.press | $glo.color.mint.50 | | +| | | color.positive.variant.disabled | $glo.color.blue.10 | $glo.transparent.t25 | +| | | color.positive.gradient.normal | linear-gradient($glo.deg.ltr,$glo.color.green.40,$glo.color.mint.60) | | +| | | color.positive.gradient.hover | linear-gradient($glo.deg.ltr,$glo.color.green.40,$glo.color.mint.50) | | +| | | color.positive.gradient.press | | | +| | | color.positive.gradient.disabled | $color.surface.nest.disabled | | +| | | color.positive.onpic.normal | $glo.color.mint.60 | $glo.transparent.t85 | +| | warning | color.warning.normal | $glo.color.orange.50 | | +| | | color.warning.hover | $glo.color.orange.40 | | +| | | color.warning.press | $glo.color.orange.60 | | +| | | color.warning.disabled | $color.surface.nest.disabled | | +| | | color.warning.variant.normal | $glo.color.orange.40 | | +| | | color.warning.variant.hover | $glo.color.orange.30 | | +| | | color.warning.variant.press | $glo.color.orange.50 | | +| | | color.warning.variant.disabled | $glo.color.blue.10 | $glo.transparent.t25 | +| | | color.warning.gradient.normal | linear-gradient($glo.deg.ltr,$glo.color.yellow.40,$glo.color.orange.50) | | +| | | color.warning.gradient.hover | linear-gradient($glo.deg.ltr,$glo.color.yellow.30,$glo.color.orange40) | | +| | | color.warning.gradient.press | linear-gradient($glo.deg.ltr,$glo.color.yellow.50,$glo.color.orange.60) | | +| | | color.warning.gradient.disabled | $color.surface.nest.disabled | | +| | | color.warning.onpic.normal | $glo.color.orange.50 | $glo.transparent.t85 | +| | emphasis | color.emphasis.normal | $glo.color.blue.40 | | +| | | color.emphasis.hover | $glo.color.blue.30 | | +| | | color.emphasis.press | $glo.color.blue.50 | | +| | | color.emphasis.disabled | $color.surface.nest.disabled | | +| | | color.emphasis.variant.normal | $glo.color.blue.30 | | +| | | color.emphasis.variant.hover | $glo.color.blue.20 | | +| | | color.emphasis.variant.press | $glo.color.blue.40 | | +| | | color.emphasis.variant.disabled | $glo.color.blue.10 | $glo.transparent.t25 | +| | | color.emphasis.gradient.normal | linear-gradient($glo.deg.ltr,$glo.color.sky.30,$glo.color.blue.40) | | +| | | color.emphasis.gradient.hover | linear-gradient($glo.deg.ltr,$glo.color.sky.20,$glo.color.blue.30) | | +| | | color.emphasis.gradient.press | linear-gradient($glo.deg.ltr,$glo.color.sky.40,$glo.color.blue.50) | | +| | | color.emphasis.gradient.disabled | $color.surface.nest.disabled | | +| | | color.emphasis.onpic.normal | $glo.color.blue.40 | $glo.transparent.t85 | +| | Background | color.background.default | $glo.color.grey.100 | | +| | | color.background.specialmap | $glo.color.grey.100 | | +| | | color.background.district | $glo.color.black | $glo.transparent.t30 | +| | Surface | color.surface.base.normal | $glo.color.grey.80 | | +| | | color.surface.base.hover | $glo.color.grey.70 | | +| | | color.surface.base.press | $glo.color.grey.90 | | +| | | color.surface.base.disabled | $glo.color.grey.90 | | +| | | color.surface.base.specialmap.normal | $glo.color.grey.80 | | +| | | color.surface.base.specialmap.hover | $glo.color.grey.70 | | +| | | color.surface.base.specialmap.press | $glo.color.grey.90 | $glo.transparent.t30 | +| | | color.surface.base.specialmap.disabled | $glo.color.white | $glo.transparent.t8 | +| | | color.surface.float.normal | $glo.color.grey.70 | | +| | | color.surface.float.hover | $glo.color.grey.60 | | +| | | color.surface.float.press | $glo.color.grey.80 | | +| | | color.surface.float.disabled | $glo.color.grey.80 | | +| | | color.surface.top.normal | $glo.color.black | $glo.transparent.t65 | +| | | color.surface.top.hover | $glo.color.black | $glo.transparent.t45 | +| | | color.surface.top.press | $glo.color.black | $glo.transparent.t85 | +| | | color.surface.top.disabled | $glo.color.black | $glo.transparent.t30 | +| | | color.surface.district.normal | $glo.color.purple.0 | $glo.transparent.t4 | +| | | color.surface.district.hover | $glo.color.purple.0 | $glo.transparent.t12 | +| | | color.surface.district.press | $glo.color.black | $glo.transparent.t25 | +| | | color.surface.district.disabled | $glo.color.black | $glo.transparent.t25 | +| | | color.surface.nest.normal | $glo.color.purple.0 | $glo.transparent.t8 | +| | | color.surface.nest.hover | $glo.color.purple.0 | $glo.transparent.t12 | +| | | color.surface.nest.press | $glo.color.purple.0 | $glo.transparent.t4 | +| | | color.surface.nest.disabled | $glo.color.purple.0 | $glo.transparent.t4 | +| | | color.surface.element.normal | $color.surface.nest.normal | | +| | | color.surface.element.hover | $color.surface.nest.hover | | +| | | color.surface.element.press | $color.surface.nest.press | | +| | | color.surface.element.disabled | $color.surface.nest.disabled | | +| | | color.surface.element.dark.normal | $glo.color.black | $glo.transparent.t65 | +| | | color.surface.element.dark.hover | $glo.color.black | $glo.transparent.t45 | +| | | color.surface.element.dark.press | $glo.color.black | $glo.transparent.t85 | +| | | color.surface.element.dark.disabled | $glo.color.black | $glo.transparent.t45 | +| | | color.surface.element.light.normal | $glo.color.white | $glo.transparent.t15 | +| | | color.surface.element.light.hover | $glo.color.white | $glo.transparent.t25 | +| | | color.surface.element.light.press | $glo.color.white | $glo.transparent.t8 | +| | | color.surface.element.light.disabled | $glo.color.white | $glo.transparent.t8 | +| | | color.surface.white.normal | $glo.color.white | | +| | | color.surface.white.hover | $glo.color.white | $glo.transparent.t85 | +| | | color.surface.white.press | $glo.color.white | $glo.transparent.t65 | +| | | color.surface.white.disabled | $glo.color.white | $glo.transparent.t45 | +| | | color.surface.black.normal | $glo.color.black | | +| | Outline | color.outline.normal | $glo.color.purple.0 | $glo.transparent.t20 | +| | | color.outline.hover | $glo.color.purple.0 | $glo.transparent.t30 | +| | | color.outline.press | $glo.color.purple.0 | $glo.transparent.t8 | +| | | color.outline.disabled | $color.surface.element.disabled | | +| | Overlay | color.overlay.primary | $glo.color.violet.30 | $glo.transparent.t30 | +| | | color.overlay.gradient | linear-gradient($glo.deg.ttb,$glo.color.black $glo.transparent.t0,$glo.color.black $glo.transparent.t100) | | +| | | color.overlay.dark | $glo.color.black | $glo.transparent.t65 | +| | | color.overlay.background | linear-gradient($glo.deg.ttb,$color.background.default $glo.transparent.t0,$color.background.default $glo.transparent.t100) | | +| | | color.overlay.base | linear-gradient($glo.deg.ttb,$color.surface.base.normal $glo.transparent.t100,$color.surface.base.normal $glo.transparent.t0) | | +| | Context | color.context.subscribe.normal | linear-gradient($glo.deg.ltr,$glo.color.purple.50,$glo.color.violet.50) | | +| | | color.context.subscribe.hover | linear-gradient($glo.deg.ltr,$glo.color.purple.30,$glo.color.violet.30) | | +| | | color.context.subscribe.press | linear-gradient($glo.deg.ltr,$glo.color.purple.70,$glo.color.violet.70) | | +| | | color.context.subscribe.disabled | $color.surface.nest.disabled | | +| | | color.context.legends.normal | linear-gradient($glo.deg.ltr,$glo.color.yellow.20,$glo.color.yellow.60) | | +| | | color.context.legends.hover | linear-gradient($glo.deg.ltr,$glo.color.yellow.10,$glo.color.yellow.40) | | +| | | color.context.legends.press | linear-gradient($glo.deg.ltr,$glo.color.yellow.60,$glo.color.yellow.90) | | +| | | color.context.legends.disabled | $color.surface.nest.disabled | | +| | | color.context.legends.variant.normal | $glo.color.yellow.20 | | +| | | color.context.legends.variant.hover | $glo.color.yellow.10 | | +| | | color.context.legends.variant.press | $glo.color.yellow.40 | | +| | | color.context.legends.variant.disabled | $color.surface.nest.disabled | | +| | | color.context.vip.normal | linear-gradient($glo.deg.ltr,$glo.color.sky.20 0%,$glo.color.violet.40 60%,$glo.color.purple.30 100%) | | +| | | color.context.recharge.normal | linear-gradient($glo.deg.ltr, $glo.color.yellow.0, $glo.color.yellow.70) | | +| | Text&icon | color.txt.primary.normal | $glo.color.white | | +| | | color.txt.primary.hover | $glo.color.magenta.30 | | +| | | color.txt.primary.press | $glo.color.magenta.40 | | +| | | color.txt.primary.disabled | $color.txt.disabled | | +| | | color.txt.primary.specialmap.normal | $glo.color.white | | +| | | color.txt.primary.specialmap.hover | $glo.color.white | $glo.transparent.t85 | +| | | color.txt.primary.specialmap.press | $glo.color.white | $glo.transparent.t65 | +| | | color.txt.primary.specialmap.disable | $glo.color.white | $glo.transparent.t45 | +| | | color.txt.secondary.normal | $glo.color.grey.30 | | +| | | color.txt.secondary.hover | $glo.color.magenta.30 | | +| | | color.txt.secondary.press | $glo.color.magenta.40 | | +| | | color.txt.secondary.disabled | $color.txt.disabled | | +| | | color.txt.tertiary.normal | $glo.color.grey.40 | | +| | | color.txt.tertiary.hover | $glo.color.grey.30 | | +| | | color.txt.tertiary | $glo.color.grey.50 | | +| | | color.txt.tertiary.disabled | $color.txt.disabled | | +| | | color.txt.grass | $glo.color.grass.40 | | +| | | color.txt.disabled | $glo.color.grey.50 | | +| Typography | display | txt.display.l | $glo.font.family.display,$glo.font.size.64,$glo.font.weight.regular,$glo.font.lineheight.size64 | | +| | | txt.display.m | $glo.font.family.display,$glo.font.size.48,$glo.font.weight.regular,$glo.font.lineheight.size48 | | +| | | txt.display.s | $glo.font.family.display,$glo.font.size.24,$glo.font.weight.regular,$glo.font.lineheight.size24 | | +| | headline | txt.headline.l | $glo.font.family.sys,$glo.font.size.48,$glo.font.weight.bold,$glo.font.lineheight.size48 | | +| | | txt.headline.m | $glo.font.family.sys,$glo.font.size.36,$glo.font.weight.bold,$glo.font.lineheight.size36 | | +| | title | txt.title.l | $glo.font.family.sys,$glo.font.size.24,$glo.font.weight.semibold,$glo.font.lineheight.size24 | | +| | | txt.title.m | $glo.font.family.sys,$glo.font.size.20,$glo.font.weight.semibold,$glo.font.lineheight.size20 | | +| | | txt.title.s | $glo.font.family.sys,$glo.font.size.16,$glo.font.weight.semibold,$glo.font.lineheight.size16 | | +| | body | txt.bodySemibold.l | $glo.font.family.sys,$glo.font.size.16,$glo.font.weight.semibold,$glo.font.lineheight.size16 | | +| | | txt.bodySemibold.m | $glo.font.family.sys,$glo.font.size.14,$glo.font.weight.semibold,$glo.font.lineheight.size14 | | +| | | txt.bodySemibold.s | $glo.font.family.sys,$glo.font.size.12,$glo.font.weight.semibold,$glo.font.lineheight.size12 | | +| | | txt.body.l | $glo.font.family.sys,$glo.font.size.16,$glo.font.weight.regular,$glo.font.lineheight.size16 | | +| | | txt.body.m | $glo.font.family.sys,$glo.font.size.14,$glo.font.weight.regular,$glo.font.lineheight.size14 | | +| | | txt.body.s | $glo.font.family.sys,$glo.font.size.12,$glo.font.weight.regular,$glo.font.lineheight.size12 | | +| | | txt.bodyItalic.l | $glo.font.family.sys,$glo.font.size.16,$glo.font.weight.regular,$glo.font.lineheight.size16,$glo.font.style.italic | | +| | label | txt.label.l | $glo.font.family.sys,$glo.font.size.16,$glo.font.weight.medium,$glo.font.lineheight.size16 | | +| | | txt.label.m | $glo.font.family.sys,$glo.font.size.14,$glo.font.weight.medium,$glo.font.lineheight.size14 | | +| | | txt.label.s | $glo.font.family.sys,$glo.font.size.12,$glo.font.weight.medium,$glo.font.lineheight.size12 | | +| | number | txt.numDisplay.xl | $glo.font.family.numDisplay,$glo.font.size.64,$glo.font.weight.bold,$glo.font.lineheight.size64 | | +| | | txt.numDisplay.l | $glo.font.family.numDisplay,$glo.font.size.48,$glo.font.weight.bold,$glo.font.lineheight.size48 | | +| | | txt.numMonotype.xl | $glo.font.family.sys,$glo.font.size.24,$glo.font.weight.bold,$glo.font.lineheight.size24 | | +| | | txt.numMonotype.l | $glo.font.family.sys,$glo.font.size.20,$glo.font.weight.bold,$glo.font.lineheight.size20 | | +| | | txt.numMonotype.m | $glo.font.family.sys,$glo.font.size.16,$glo.font.weight.bold,$glo.font.lineheight.size16 | | +| | | txt.numMonotype.s | $glo.font.family.sys,$glo.font.size.14,$glo.font.weight.medium,$glo.font.lineheight.size14 | | +| | | txt.numMonotype.xs | $glo.font.family.sys,$glo.font.size.12,$glo.font.weight.regular,$glo.font.lineheight.size12 | | +| visual style | shadow | shadow.s | @SHA:$glo.color.black,$$glo.transparent.t45,0&0,4,渐变色,ShadowOpacity,便宜,半径 | | +| | | shadow.m | @SHA:$glo.color.black,$$glo.transparent.t45,0&0,8 | | +| | | shadow.l | @SHA:$glo.color.black,$$glo.transparent.t45,0&0,16 | | +| | radius | radius.xs | $glo.radius.4 | | +| | | radius.s | $glo.radius.8 | | +| | | radius.m | $glo.radius.12 | | +| | | radius.l | $glo.radius.16 | | +| | | radius.xl | $glo.radius.24 | | +| | | radius.round | $glo.radius.round | | +| | | radius.pill | $glo.radius.round | | +| | border | border.divider | $glo.border.1 | | +| | | border.s | $glo.border.1 | | +| | | border.m | $glo.border.2 | | +| | | border.l | $glo.border.4 | | diff --git a/docs/EnvironmentVariables.md b/docs/EnvironmentVariables.md new file mode 100644 index 0000000..6574ff3 --- /dev/null +++ b/docs/EnvironmentVariables.md @@ -0,0 +1,173 @@ +# 环境变量配置说明 + +## 概述 + +本文档说明项目所需的环境变量配置。请在项目根目录创建 `.env.local` 文件并添加以下配置。 + +## 必需的环境变量 + +### 应用配置 + +```bash +# 应用的基础 URL +# 开发环境: http://localhost:3000 +# 生产环境: https://test.crushlevel.ai 或您的域名 +NEXT_PUBLIC_APP_URL=https://test.crushlevel.ai +``` + +### Discord OAuth 配置 + +```bash +# Discord OAuth 客户端 ID +# 从 Discord Developer Portal 获取 +# https://discord.com/developers/applications +NEXT_PUBLIC_DISCORD_CLIENT_ID=your_discord_client_id_here +``` + +**获取步骤**: +1. 访问 [Discord Developer Portal](https://discord.com/developers/applications) +2. 创建或选择应用 +3. 在 OAuth2 设置中配置回调 URL: + - `https://test.crushlevel.ai/api/auth/discord/callback` + - `http://localhost:3000/api/auth/discord/callback` (开发环境) +4. 复制 Client ID + +### Google OAuth 配置 + +```bash +# Google OAuth 客户端 ID +# 从 Google Cloud Console 获取 +# https://console.cloud.google.com/ +NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id_here +``` + +**获取步骤**: +1. 访问 [Google Cloud Console](https://console.cloud.google.com/) +2. 创建或选择项目 +3. 启用 Google+ API +4. 创建 OAuth 2.0 客户端 ID +5. 配置授权重定向 URI: + - `https://test.crushlevel.ai/api/auth/google/callback` + - `http://localhost:3000/api/auth/google/callback` (开发环境) +6. 复制客户端 ID + +## 环境变量文件示例 + +创建 `.env.local` 文件: + +```bash +# .env.local + +# App Configuration +NEXT_PUBLIC_APP_URL=http://localhost:3000 + +# Discord OAuth +NEXT_PUBLIC_DISCORD_CLIENT_ID=1234567890123456789 + +# Google OAuth +NEXT_PUBLIC_GOOGLE_CLIENT_ID=1234567890-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com +``` + +## 不同环境的配置 + +### 开发环境 (`.env.local`) + +```bash +NEXT_PUBLIC_APP_URL=http://localhost:3000 +NEXT_PUBLIC_DISCORD_CLIENT_ID=dev_discord_client_id +NEXT_PUBLIC_GOOGLE_CLIENT_ID=dev_google_client_id +``` + +### 生产环境 (`.env.production`) + +```bash +NEXT_PUBLIC_APP_URL=https://test.crushlevel.ai +NEXT_PUBLIC_DISCORD_CLIENT_ID=prod_discord_client_id +NEXT_PUBLIC_GOOGLE_CLIENT_ID=prod_google_client_id +``` + +## 安全注意事项 + +### ⚠️ 重要提醒 + +1. **不要提交敏感信息到 Git** + - `.env.local` 已在 `.gitignore` 中 + - 不要将真实的密钥提交到代码库 + +2. **使用不同的凭据** + - 开发环境和生产环境使用不同的 OAuth 凭据 + - 定期轮换生产环境的密钥 + +3. **限制回调 URL** + - 只添加必要的回调 URL + - 不要使用通配符 + +4. **环境变量前缀** + - `NEXT_PUBLIC_` 前缀的变量会暴露到浏览器 + - 敏感的服务端密钥不要使用此前缀 + +## 验证配置 + +### 检查环境变量是否正确加载 + +在组件中添加调试代码(仅开发环境): + +```typescript +console.log('App URL:', process.env.NEXT_PUBLIC_APP_URL) +console.log('Discord Client ID:', process.env.NEXT_PUBLIC_DISCORD_CLIENT_ID) +console.log('Google Client ID:', process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) +``` + +### 常见问题 + +**问题 1**: 环境变量未生效 + +**解决方案**: +- 重启开发服务器 (`npm run dev`) +- 确保文件名正确 (`.env.local`) +- 检查变量名拼写 + +**问题 2**: OAuth 回调失败 + +**解决方案**: +- 检查 `NEXT_PUBLIC_APP_URL` 是否正确 +- 确保回调 URL 在 OAuth 提供商处正确配置 +- 开发环境使用 `http://localhost:3000`,生产环境使用实际域名 + +**问题 3**: 客户端 ID 无效 + +**解决方案**: +- 确认复制的是 Client ID 而不是 Client Secret +- 检查是否有多余的空格或换行符 +- 确认 OAuth 应用状态为已发布/激活 + +## 添加新的环境变量 + +当添加新的环境变量时: + +1. 在 `.env.local` 中添加变量 +2. 更新本文档 +3. 通知团队成员更新他们的本地配置 +4. 在部署平台(Vercel/Netlify 等)配置生产环境变量 + +## 部署平台配置 + +### Vercel + +1. 进入项目设置 +2. 选择 "Environment Variables" +3. 添加所有 `NEXT_PUBLIC_*` 变量 +4. 为不同环境(Production/Preview/Development)设置不同的值 + +### Netlify + +1. 进入 Site settings +2. 选择 "Build & deploy" > "Environment" +3. 添加环境变量 + +## 相关文档 + +- [Next.js 环境变量文档](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables) +- [Discord OAuth 文档](https://discord.com/developers/docs/topics/oauth2) +- [Google OAuth 文档](https://developers.google.com/identity/protocols/oauth2) + diff --git a/docs/GoogleOAuth-GIS.md b/docs/GoogleOAuth-GIS.md new file mode 100644 index 0000000..073e36d --- /dev/null +++ b/docs/GoogleOAuth-GIS.md @@ -0,0 +1,391 @@ +# Google Identity Services (GIS) 登录集成文档 + +## 概述 + +使用最新的 **Google Identity Services (GIS)** SDK 实现 Google 登录,无需页面跳转,通过弹窗方式完成授权,用户体验更好。 + +## 新旧方式对比 + +### 旧方式(OAuth 2.0 重定向流程) +``` +用户点击按钮 → 跳转到 Google 授权页面 → 授权后重定向回应用 +``` +❌ 需要页面跳转 +❌ 需要配置回调路由 +❌ 用户体验不连贯 + +### 新方式(Google Identity Services) +``` +用户点击按钮 → 弹出 Google 授权窗口 → 授权后直接回调 +``` +✅ 无需页面跳转 +✅ 无需回调路由 +✅ 用户体验流畅 +✅ 更安全(弹窗隔离) + +## 实现架构 + +### 工作流程 + +``` +用户点击 "Continue with Google" + ↓ +加载 Google Identity Services SDK + ↓ +初始化 Code Client + ↓ +弹出 Google 授权窗口 + ↓ +用户授权 + ↓ +回调函数接收授权码 + ↓ +调用后端登录接口 + ↓ +登录成功,跳转到首页 +``` + +## 核心文件 + +### 1. Google OAuth 配置 (`src/lib/oauth/google.ts`) + +**主要功能**: +- 定义 Google Identity Services 的 TypeScript 类型 +- 提供 SDK 加载方法 +- 提供 Code Client 初始化方法 + +**关键代码**: +```typescript +export const googleOAuth = { + // 加载 Google Identity Services SDK + loadScript: (): Promise => { + return new Promise((resolve, reject) => { + if (window.google?.accounts) { + resolve() + return + } + + const script = document.createElement('script') + script.src = 'https://accounts.google.com/gsi/client' + script.async = true + script.defer = true + script.onload = () => resolve() + script.onerror = () => reject(new Error('Failed to load SDK')) + + document.head.appendChild(script) + }) + }, + + // 初始化 Code Client(获取授权码) + initCodeClient: (callback, errorCallback) => { + return window.google.accounts.oauth2.initCodeClient({ + client_id: GOOGLE_CLIENT_ID, + scope: GOOGLE_SCOPES, + ux_mode: 'popup', + callback, + error_callback: errorCallback + }) + } +} +``` + +### 2. GoogleButton 组件 (`src/app/(auth)/login/components/GoogleButton.tsx`) + +**主要功能**: +- 加载 Google Identity Services SDK +- 初始化 Code Client +- 处理授权码回调 +- 调用后端登录接口 + +**关键实现**: + +#### SDK 加载 +```typescript +useEffect(() => { + const loadGoogleSDK = async () => { + try { + await googleOAuth.loadScript() + console.log('Google Identity Services SDK loaded') + } catch (error) { + console.error('Failed to load Google SDK:', error) + toast.error("Failed to load Google login") + } + } + + loadGoogleSDK() +}, []) +``` + +#### 授权码处理 +```typescript +const handleGoogleResponse = async (response: GoogleCodeResponse) => { + const deviceId = tokenManager.getDeviceId() + const loginData = { + appClient: AppClient.Web, + deviceCode: deviceId, + thirdToken: response.code, // Google 授权码 + thirdType: ThirdType.Google + } + + login.mutate(loginData, { + onSuccess: () => { + toast.success("Login successful") + router.push('/') + }, + onError: (error) => { + toast.error("Login failed") + } + }) +} +``` + +#### 登录按钮点击 +```typescript +const handleGoogleLogin = async () => { + // 确保 SDK 已加载 + if (!window.google?.accounts?.oauth2) { + await googleOAuth.loadScript() + } + + // 初始化 Code Client + if (!codeClientRef.current) { + codeClientRef.current = googleOAuth.initCodeClient( + handleGoogleResponse, + handleGoogleError + ) + } + + // 请求授权码(弹出授权窗口) + codeClientRef.current.requestCode() +} +``` + +## 环境变量配置 + +只需要配置客户端 ID,不需要配置回调 URL: + +```bash +# .env.local +NEXT_PUBLIC_GOOGLE_CLIENT_ID=你的Google客户端ID +``` + +### 获取 Google OAuth 凭据 + +1. 访问 [Google Cloud Console](https://console.cloud.google.com/) +2. 创建或选择项目 +3. 启用 Google+ API +4. 创建 OAuth 2.0 客户端 ID +5. 应用类型选择 "Web 应用" +6. **授权的 JavaScript 来源**添加: + ``` + http://localhost:3000 + https://test.crushlevel.ai + ``` +7. **授权的重定向 URI** 可以留空(GIS 不需要) +8. 复制客户端 ID + +## 后端接口要求 + +与之前相同,后端接收授权码并完成登录: + +```typescript +POST /api/auth/login + +{ + "appClient": "WEB", + "deviceCode": "设备ID", + "thirdToken": "Google授权码", + "thirdType": "GOOGLE" +} +``` + +后端需要: +1. 使用授权码向 Google 交换 access_token +2. 使用 access_token 获取用户信息 +3. 创建或更新用户 +4. 返回应用的登录 token + +## 优势 + +### 1. 更好的用户体验 +- ✅ 无需离开当前页面 +- ✅ 弹窗授权,快速完成 +- ✅ 不打断用户操作流程 + +### 2. 更简单的实现 +- ✅ 不需要回调路由 +- ✅ 不需要处理 URL 参数 +- ✅ 不需要 state 验证 +- ✅ 代码更简洁 + +### 3. 更安全 +- ✅ 弹窗隔离,防止钓鱼 +- ✅ SDK 自动处理安全验证 +- ✅ 支持 CORS 和 CSP + +### 4. 更现代 +- ✅ Google 官方推荐方式 +- ✅ 持续维护和更新 +- ✅ 更好的浏览器兼容性 + +## 与旧实现的对比 + +| 特性 | 旧方式(重定向) | 新方式(GIS) | +|------|----------------|--------------| +| 页面跳转 | ✅ 需要 | ❌ 不需要 | +| 回调路由 | ✅ 需要 | ❌ 不需要 | +| State 验证 | ✅ 需要手动实现 | ❌ SDK 自动处理 | +| URL 参数处理 | ✅ 需要 | ❌ 不需要 | +| 用户体验 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | +| 代码复杂度 | 高 | 低 | +| 维护成本 | 高 | 低 | + +## 常见问题 + +### Q: SDK 加载失败怎么办? +A: +- 检查网络连接 +- 确认没有被广告拦截器阻止 +- 检查浏览器控制台错误信息 + +### Q: 弹窗被浏览器拦截? +A: +- 确保在用户点击事件中调用 `requestCode()` +- 不要在异步操作后调用 +- 检查浏览器弹窗设置 + +### Q: 授权后没有回调? +A: +- 检查回调函数是否正确绑定 +- 查看浏览器控制台是否有错误 +- 确认 Client ID 配置正确 + +### Q: 用户取消授权如何处理? +A: +```typescript +const handleGoogleError = (error: any) => { + // 用户取消授权不显示错误提示 + if (error.type === 'popup_closed') { + return + } + + toast.error("Google login failed") +} +``` + +## 测试清单 + +### 本地测试 +- [ ] SDK 正常加载 +- [ ] 点击按钮弹出授权窗口 +- [ ] 授权后正确回调 +- [ ] 授权码正确传递给后端 +- [ ] 登录成功后正确跳转 +- [ ] 用户取消授权的处理 +- [ ] 错误情况的处理 + +### 生产环境测试 +- [ ] 配置正确的 JavaScript 来源 +- [ ] HTTPS 证书有效 +- [ ] 环境变量配置正确 +- [ ] 后端接口正常工作 +- [ ] 不同浏览器测试 + +## 浏览器兼容性 + +Google Identity Services 支持: +- ✅ Chrome 90+ +- ✅ Firefox 88+ +- ✅ Safari 14+ +- ✅ Edge 90+ + +## 安全注意事项 + +### 1. 客户端 ID 保护 +虽然客户端 ID 是公开的,但仍需注意: +- 限制授权的 JavaScript 来源 +- 定期检查使用情况 +- 发现异常及时更换 + +### 2. 授权码处理 +- 授权码只能使用一次 +- 及时传递给后端 +- 不要在客户端存储 + +### 3. HTTPS 要求 +- 生产环境必须使用 HTTPS +- 本地开发可以使用 HTTP + +## 迁移指南 + +如果你之前使用的是旧的重定向方式,迁移步骤: + +1. **更新配置文件** + - 使用新的 `src/lib/oauth/google.ts` + +2. **更新组件** + - 使用新的 `GoogleButton.tsx` + +3. **删除回调路由** + - 删除 `src/app/api/auth/google/callback/route.ts` + +4. **更新 Google Cloud Console** + - 添加授权的 JavaScript 来源 + - 可以移除重定向 URI(可选) + +5. **测试** + - 完整测试登录流程 + - 确认所有功能正常 + +## 扩展功能 + +### 1. One Tap 登录 +可以添加 Google One Tap 功能,自动显示登录提示: + +```typescript +window.google.accounts.id.initialize({ + client_id: GOOGLE_CLIENT_ID, + callback: handleCredentialResponse +}) + +window.google.accounts.id.prompt() +``` + +### 2. 自动登录 +可以实现自动登录功能: + +```typescript +window.google.accounts.id.initialize({ + client_id: GOOGLE_CLIENT_ID, + callback: handleCredentialResponse, + auto_select: true +}) +``` + +### 3. 自定义按钮样式 +可以使用 Google 提供的标准按钮: + +```typescript +window.google.accounts.id.renderButton( + document.getElementById('buttonDiv'), + { theme: 'outline', size: 'large' } +) +``` + +## 相关文档 + +- [Google Identity Services 官方文档](https://developers.google.com/identity/gsi/web) +- [Code Model 文档](https://developers.google.com/identity/oauth2/web/guides/use-code-model) +- [迁移指南](https://developers.google.com/identity/gsi/web/guides/migration) + +## 总结 + +使用 Google Identity Services 是 Google 官方推荐的最新方式,相比传统的 OAuth 重定向流程: + +✅ **用户体验更好** - 无需页面跳转 +✅ **实现更简单** - 代码量更少 +✅ **维护更容易** - 无需处理复杂的回调 +✅ **更加安全** - SDK 自动处理安全验证 + +强烈建议新项目直接使用这种方式! + diff --git a/docs/GoogleOAuth-QuickStart.md b/docs/GoogleOAuth-QuickStart.md new file mode 100644 index 0000000..68bea00 --- /dev/null +++ b/docs/GoogleOAuth-QuickStart.md @@ -0,0 +1,136 @@ +# Google OAuth 快速开始指南 + +## 5 分钟快速集成 + +### 步骤 1: 获取 Google OAuth 凭据 + +1. 访问 [Google Cloud Console](https://console.cloud.google.com/) +2. 创建新项目或选择现有项目 +3. 在左侧菜单选择 "API 和服务" > "凭据" +4. 点击 "创建凭据" > "OAuth 客户端 ID" +5. 选择应用类型为 "Web 应用" +6. 配置授权重定向 URI: + ``` + http://localhost:3000/api/auth/google/callback + ``` +7. 点击"创建"并复制客户端 ID + +### 步骤 2: 配置环境变量 + +在项目根目录创建 `.env.local` 文件: + +```bash +NEXT_PUBLIC_APP_URL=http://localhost:3000 +NEXT_PUBLIC_GOOGLE_CLIENT_ID=你的客户端ID +``` + +### 步骤 3: 重启开发服务器 + +```bash +npm run dev +``` + +### 步骤 4: 测试登录 + +1. 访问 http://localhost:3000/login +2. 点击 "Continue with Google" 按钮 +3. 选择 Google 账号并授权 +4. 应该会重定向回应用并完成登录 + +## 文件清单 + +已创建的文件: +- ✅ `src/lib/oauth/google.ts` - Google OAuth 配置 +- ✅ `src/app/(auth)/login/components/GoogleButton.tsx` - Google 登录按钮组件 +- ✅ `src/app/api/auth/google/callback/route.ts` - OAuth 回调路由 +- ✅ `src/app/(auth)/login/components/login-form.tsx` - 已更新使用 GoogleButton + +## 工作流程 + +``` +用户点击按钮 + ↓ +GoogleButton.handleGoogleLogin() + ↓ +跳转到 Google 授权页面 + ↓ +用户授权 + ↓ +Google 重定向到 /api/auth/google/callback + ↓ +API 路由提取 code 并重定向到 /login?google_code=xxx + ↓ +GoogleButton.useEffect() 检测到 google_code + ↓ +调用后端登录接口 (thirdType: Google) + ↓ +登录成功,跳转到首页 +``` + +## 后端接口要求 + +后端需要处理以下登录请求: + +```typescript +POST /api/auth/login + +{ + "appClient": "WEB", + "deviceCode": "设备ID", + "thirdToken": "Google授权码", + "thirdType": "GOOGLE" +} +``` + +后端需要: +1. 使用授权码向 Google 交换 access_token +2. 使用 access_token 获取用户信息 +3. 创建或更新用户 +4. 返回应用的登录 token + +## 常见问题 + +### Q: 点击按钮后没有跳转? +A: 检查浏览器控制台是否有错误,确认环境变量已正确配置。 + +### Q: 回调后显示错误? +A: 检查 Google Cloud Console 中的回调 URL 配置是否正确。 + +### Q: 登录接口调用失败? +A: 确认后端接口已实现并支持 Google 登录。 + +## 生产环境部署 + +### 1. 更新 Google OAuth 配置 + +在 Google Cloud Console 添加生产环境回调 URL: +``` +https://your-domain.com/api/auth/google/callback +``` + +### 2. 更新环境变量 + +```bash +NEXT_PUBLIC_APP_URL=https://your-domain.com +NEXT_PUBLIC_GOOGLE_CLIENT_ID=生产环境客户端ID +``` + +### 3. 部署 + +确保在部署平台(Vercel/Netlify)配置了正确的环境变量。 + +## 下一步 + +- [ ] 测试完整的登录流程 +- [ ] 添加错误处理和用户反馈 +- [ ] 实现登出功能 +- [ ] 添加用户信息展示 +- [ ] 考虑添加 Apple 登录 + +## 技术支持 + +如有问题,请参考: +- [完整文档](./GoogleOAuth.md) +- [环境变量配置](./EnvironmentVariables.md) +- [Google OAuth 官方文档](https://developers.google.com/identity/protocols/oauth2) + diff --git a/docs/GoogleOAuth.md b/docs/GoogleOAuth.md new file mode 100644 index 0000000..b3e97fd --- /dev/null +++ b/docs/GoogleOAuth.md @@ -0,0 +1,289 @@ +# Google OAuth 登录集成文档 + +## 功能概述 + +实现了 Google OAuth 2.0 登录功能,参考 Discord 登录的实现模式,用户可以通过 Google 账号快速登录应用。 + +## 实现架构 + +### 1. OAuth 流程 + +``` +用户点击 "Continue with Google" + ↓ +跳转到 Google 授权页面 + ↓ +用户授权后,Google 重定向到回调 URL + ↓ +API 路由接收授权码并重定向回登录页 + ↓ +前端获取授权码并调用后端登录接口 + ↓ +登录成功,跳转到首页或指定页面 +``` + +## 文件结构 + +``` +src/ +├── lib/ +│ └── oauth/ +│ ├── discord.ts # Discord OAuth 配置 +│ └── google.ts # Google OAuth 配置 (新增) +├── app/ +│ ├── (auth)/ +│ │ └── login/ +│ │ └── components/ +│ │ ├── DiscordButton.tsx # Discord 登录按钮 +│ │ ├── GoogleButton.tsx # Google 登录按钮 (新增) +│ │ └── login-form.tsx # 登录表单 (已更新) +│ └── api/ +│ └── auth/ +│ ├── discord/ +│ │ └── callback/ +│ │ └── route.ts # Discord 回调路由 +│ └── google/ +│ └── callback/ +│ └── route.ts # Google 回调路由 (新增) +``` + +## 核心文件说明 + +### 1. Google OAuth 配置 (`src/lib/oauth/google.ts`) + +```typescript +export const googleOAuth = { + getAuthUrl: (state?: string): string => { + // 构建 Google OAuth 授权 URL + // 包含 client_id, redirect_uri, scope 等参数 + } +} +``` + +**配置参数**: +- `client_id`: Google OAuth 客户端 ID +- `redirect_uri`: 授权后的回调 URL +- `scope`: 请求的权限范围(email, profile) +- `access_type`: offline(获取 refresh_token) +- `prompt`: consent(每次都显示授权页面) + +### 2. GoogleButton 组件 (`src/app/(auth)/login/components/GoogleButton.tsx`) + +**功能**: +- 处理 Google 登录按钮点击事件 +- 生成随机 state 用于安全验证 +- 跳转到 Google 授权页面 +- 处理 OAuth 回调(授权码) +- 调用后端登录接口 +- 处理登录成功/失败的重定向 + +**关键方法**: +```typescript +const handleGoogleLogin = () => { + // 1. 生成 state + const state = Math.random().toString(36).substring(2, 15) + + // 2. 获取授权 URL + const authUrl = googleOAuth.getAuthUrl(state) + + // 3. 保存 state 到 sessionStorage + sessionStorage.setItem('google_oauth_state', state) + + // 4. 跳转到 Google 授权页面 + window.location.href = authUrl +} +``` + +**OAuth 回调处理**: +```typescript +useEffect(() => { + const googleCode = searchParams.get('google_code') + const googleState = searchParams.get('google_state') + + if (googleCode) { + // 验证 state + // 调用后端登录接口 + // 处理登录结果 + } +}, []) +``` + +### 3. Google 回调路由 (`src/app/api/auth/google/callback/route.ts`) + +**功能**: +- 接收 Google OAuth 回调 +- 提取授权码 (code) 和 state +- 重定向回登录页面,并将参数传递给前端 + +```typescript +export async function GET(request: NextRequest) { + const code = searchParams.get('code') + const state = searchParams.get('state') + + // 重定向到登录页,携带 google_code 和 google_state + redirectUrl.searchParams.set('google_code', code) + redirectUrl.searchParams.set('google_state', state) + + return NextResponse.redirect(redirectUrl) +} +``` + +## 环境变量配置 + +需要在 `.env.local` 中添加以下环境变量: + +```bash +# Google OAuth 配置 +NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id_here +NEXT_PUBLIC_APP_URL=https://test.crushlevel.ai +``` + +### 获取 Google OAuth 凭据 + +1. 访问 [Google Cloud Console](https://console.cloud.google.com/) +2. 创建或选择一个项目 +3. 启用 Google+ API +4. 创建 OAuth 2.0 客户端 ID +5. 配置授权重定向 URI: + ``` + https://test.crushlevel.ai/api/auth/google/callback + http://localhost:3000/api/auth/google/callback (开发环境) + ``` +6. 复制客户端 ID 到环境变量 + +## 后端接口要求 + +后端需要实现登录接口,接收以下参数: + +```typescript +interface LoginRequest { + appClient: AppClient.Web + deviceCode: string // 设备唯一标识 + thirdToken: string // Google 授权码 + thirdType: ThirdType.Google // 第三方类型 +} +``` + +后端需要: +1. 使用授权码向 Google 交换 access_token +2. 使用 access_token 获取用户信息 +3. 创建或更新用户账号 +4. 返回应用的登录 token + +## 安全特性 + +### 1. State 参数验证 +- 前端生成随机 state 并保存到 sessionStorage +- 回调时验证 state 是否匹配 +- 防止 CSRF 攻击 + +### 2. 授权码模式 +- 使用 OAuth 2.0 授权码流程 +- 授权码只能使用一次 +- Token 交换在后端进行,更安全 + +### 3. URL 参数清理 +- 登录成功后清理 URL 中的敏感参数 +- 防止参数泄露 + +## 用户体验优化 + +### 1. 重定向保持 +```typescript +// 保存登录前的页面 +sessionStorage.setItem('login_redirect_url', redirect || '') + +// 登录成功后跳转回原页面 +const loginRedirectUrl = sessionStorage.getItem('login_redirect_url') +if (loginRedirectUrl) { + router.push(loginRedirectUrl) +} +``` + +### 2. 错误处理 +- 授权失败时显示友好的错误提示 +- 自动清理 URL 参数 +- 不影响用户继续尝试登录 + +### 3. 加载状态 +- 使用 `useLogin` Hook 的 loading 状态 +- 可以添加 loading 动画提升体验 + +## 测试清单 + +### 本地测试 +- [ ] 点击 Google 登录按钮跳转到 Google 授权页面 +- [ ] 授权后正确回调到应用 +- [ ] 授权码正确传递给后端 +- [ ] 登录成功后跳转到首页 +- [ ] State 参数验证正常工作 +- [ ] 错误情况处理正确 + +### 生产环境测试 +- [ ] 配置正确的回调 URL +- [ ] HTTPS 证书有效 +- [ ] 环境变量配置正确 +- [ ] 后端接口正常工作 + +## 常见问题 + +### 1. 回调 URL 不匹配 +**错误**: `redirect_uri_mismatch` + +**解决方案**: +- 检查 Google Cloud Console 中配置的回调 URL +- 确保 `NEXT_PUBLIC_APP_URL` 环境变量正确 +- 开发环境和生产环境需要分别配置 + +### 2. State 验证失败 +**错误**: "Google login failed" + +**解决方案**: +- 检查 sessionStorage 是否正常工作 +- 确保没有跨域问题 +- 检查浏览器是否禁用了 cookie/storage + +### 3. 授权码已使用 +**错误**: 后端返回授权码无效 + +**解决方案**: +- 授权码只能使用一次 +- 避免重复调用登录接口 +- 清理 URL 参数防止页面刷新时重复使用 + +## 与 Discord 登录的对比 + +| 特性 | Discord | Google | +|------|---------|--------| +| OAuth Provider | Discord | Google | +| Scopes | identify, email | userinfo.email, userinfo.profile | +| 授权 URL | discord.com/api/oauth2/authorize | accounts.google.com/o/oauth2/v2/auth | +| 回调路由 | /api/auth/discord/callback | /api/auth/google/callback | +| URL 参数 | discord_code, discord_state | google_code, google_state | +| ThirdType | Discord | Google | + +## 扩展建议 + +### 1. 添加 Apple 登录 +参考 Google 登录的实现,创建: +- `src/lib/oauth/apple.ts` +- `src/app/(auth)/login/components/AppleButton.tsx` +- `src/app/api/auth/apple/callback/route.ts` + +### 2. 统一 OAuth 处理 +可以创建通用的 OAuth Hook: +```typescript +const useOAuthLogin = (provider: 'google' | 'discord' | 'apple') => { + // 通用的 OAuth 登录逻辑 +} +``` + +### 3. 添加登录统计 +记录不同登录方式的使用情况,优化用户体验。 + +## 相关文档 + +- [Google OAuth 2.0 文档](https://developers.google.com/identity/protocols/oauth2) +- [Next.js API Routes](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) +- Discord OAuth 实现参考 + diff --git a/docs/MessageLikeFeature.md b/docs/MessageLikeFeature.md new file mode 100644 index 0000000..b0555d2 --- /dev/null +++ b/docs/MessageLikeFeature.md @@ -0,0 +1,244 @@ +# 消息点赞功能实现 + +## 概述 + +本功能基于网易云信 NIM Web SDK V2 实现了聊天消息的点赞和踩功能,通过更新消息的 `serverExtension` 字段来持久化存储用户的点赞状态,使用 NIM SDK 的 `modifyMessage` API 实现服务端同步。 + +## 功能特性 + +- ✅ 支持对AI回复消息进行点赞/踩 +- ✅ 简洁的状态标记:只记录用户的点赞/踩状态,不统计数量 +- ✅ 视觉反馈:点赞后按钮高亮显示 +- ✅ 防重复点赞:再次点击取消点赞 +- ✅ 状态持久化:通过 NIM SDK 的 `serverExtension` 字段保存到云端 +- ✅ 多用户支持:支持多个用户对同一消息进行独立的点赞 +- ✅ 自动同步:点赞状态自动同步到所有客户端 +- ✅ 轻量级:简化数据结构,减少存储空间 + +## 技术实现 + +### 1. 数据结构 + +#### MessageLikeStatus 枚举 +```typescript +export enum MessageLikeStatus { + None = 'none', // 未点赞/踩 + Liked = 'liked', // 已点赞 + Disliked = 'disliked' // 已踩 +} +``` + +#### MessageServerExtension 接口(简化版) +```typescript +export interface MessageServerExtension { + [userId: string]: MessageLikeStatus; // 用户ID -> 点赞状态的直接映射 +} +``` + +#### 工具函数 +```typescript +// 解析消息的serverExtension字段 +export const parseMessageServerExtension = (serverExtension?: string): MessageServerExtension + +// 序列化MessageServerExtension对象 +export const stringifyMessageServerExtension = (extension: MessageServerExtension): string + +// 获取用户对消息的点赞状态 +export const getUserLikeStatus = (message: ExtendedMessage, userId: string): MessageLikeStatus +``` + +### 2. 核心功能 + +#### NimMsgContext 扩展 +在 `NimMsgContext` 中添加了 `updateMessageLikeStatus` 方法,使用 NIM SDK 的 `modifyMessage` API: + +```typescript +const updateMessageLikeStatus = useCallback(async ( + conversationId: string, + messageClientId: string, + likeStatus: MessageLikeStatus +) => { + // 1. 获取当前登录用户ID + const currentUserId = nim.V2NIMLoginService.getLoginUser(); + + // 2. 解析当前消息的serverExtension + const currentServerExt = parseMessageServerExtension(targetMessage.serverExtension); + + // 3. 更新用户的点赞状态(简化版) + const newServerExt = { ...currentServerExt }; + if (likeStatus === MessageLikeStatus.None) { + delete newServerExt[currentUserId]; // 移除点赞状态 + } else { + newServerExt[currentUserId] = likeStatus; // 设置新状态 + } + + // 4. 调用NIM SDK更新消息 + const modifyResult = await nim.V2NIMMessageService.modifyMessage(targetMessage, { + serverExtension: stringifyMessageServerExtension(newServerExt) + }); + + // 5. 更新本地状态 + addMsg(conversationId, [modifyResult.message], false); +}, [addMsg]); +``` + +#### useMessageLike Hook +提供便捷的点赞操作方法: + +```typescript +const { + likeMessage, // 点赞消息 + dislikeMessage, // 踩消息 + cancelLikeMessage, // 取消点赞/踩 + toggleLike, // 切换点赞状态 + toggleDislike, // 切换踩状态 +} = useMessageLike(); +``` + +### 3. UI组件 + +#### ChatOtherTextContainer +AI消息容器组件已集成点赞功能: + +- 鼠标悬停显示操作按钮 +- 点赞后按钮高亮(红色) +- 踩后按钮高亮(灰色) +- 显示点赞/踩数量 + +## 使用方法 + +### 基本用法 + +```typescript +import { useMessageLike } from '@/hooks/useMessageLike'; +import { getUserLikeStatus, MessageLikeStatus } from '@/atoms/im'; +import { useNimChat } from '@/context/NimChat/useNimChat'; + +const MyComponent = ({ message }: { message: ExtendedMessage }) => { + const { toggleLike, toggleDislike } = useMessageLike(); + const { nim } = useNimChat(); + + // 获取当前用户的点赞状态 + const currentUserId = nim.V2NIMLoginService.getLoginUser(); + const currentStatus = getUserLikeStatus(message, currentUserId || ''); + + const handleLike = async () => { + await toggleLike(message.conversationId, message.messageClientId, currentStatus); + }; + + const handleDislike = async () => { + await toggleDislike(message.conversationId, message.messageClientId, currentStatus); + }; + + return ( +
+ + +
+ ); +}; +``` + +### 高级用法 + +```typescript +// 直接设置点赞状态 +await likeMessage(conversationId, messageClientId); + +// 直接设置踩状态 +await dislikeMessage(conversationId, messageClientId); + +// 取消所有状态 +await cancelLikeMessage(conversationId, messageClientId); +``` + +## 状态管理 + +点赞状态通过以下方式管理: + +1. **服务端状态**: 存储在 `message.serverExtension` 字段中,通过 NIM SDK 同步到云端 +2. **多用户支持**: 每个用户的点赞状态独立存储,使用用户ID作为键 +3. **简化存储**: 仅存储用户的点赞状态,不计算总数,节省存储空间 +4. **状态同步**: 通过 `msgListAtom` 全局状态管理,并通过 NIM SDK 自动同步到所有客户端 +5. **持久化**: 点赞状态持久化存储在 NIM 服务器,不会丢失 + +## 扩展建议 + +### 1. 消息更新监听 +由于使用了 NIM SDK 的 `modifyMessage` API,建议监听消息更新事件: + +```typescript +// 监听消息修改事件 +nim.V2NIMMessageService.on('onMessageUpdated', (messages: V2NIMMessage[]) => { + messages.forEach(message => { + // 处理点赞状态更新 + const serverExt = parseMessageServerExtension(message.serverExtension); + if (serverExt.likes) { + console.log('消息点赞状态已更新:', message.messageClientId, serverExt); + } + }); +}); +``` + +### 2. 错误处理 +为点赞操作添加错误处理和重试机制: + +```typescript +const updateMessageLikeStatusWithRetry = async ( + conversationId: string, + messageClientId: string, + likeStatus: MessageLikeStatus, + retryCount = 3 +) => { + try { + await updateMessageLikeStatus(conversationId, messageClientId, likeStatus); + } catch (error) { + if (retryCount > 0) { + console.log(`点赞失败,剩余重试次数: ${retryCount}`, error); + await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒 + return updateMessageLikeStatusWithRetry(conversationId, messageClientId, likeStatus, retryCount - 1); + } else { + throw error; + } + } +}; +``` + +### 3. 批量操作 +对于大量消息的点赞状态批量更新: + +```typescript +const batchUpdateLikes = (updates: Array<{ + conversationId: string; + messageClientId: string; + likeStatus: MessageLikeStatus; +}>) => { + // 批量更新逻辑 +}; +``` + +## 注意事项 + +1. **性能考虑**: 点赞状态更新会触发组件重渲染,建议使用 React.memo 优化 +2. **网络请求**: 每次点赞都会调用 NIM SDK 的 `modifyMessage` API,请考虑网络状况 +3. **并发控制**: 快速连续点击可能导致并发请求,建议添加防抖或节流 +4. **权限验证**: NIM SDK 会自动验证用户权限,无需额外处理 +5. **消息类型限制**: `modifyMessage` API 仅支持特定类型的消息,请参考 NIM 文档 +6. **扩展字段大小**: `serverExtension` 字段有大小限制,请合理设计数据结构 + +## 相关文件 + +- `src/atoms/im.ts` - 数据类型定义 +- `src/context/NimChat/NimMsgContext.tsx` - 核心逻辑 +- `src/hooks/useMessageLike.ts` - 便捷Hook +- `src/app/(main)/chat/[aiId]/components/ChatMessageItems/ChatOtherTextContainer.tsx` - UI实现 diff --git a/docs/URLTextParameter.md b/docs/URLTextParameter.md new file mode 100644 index 0000000..d078e7c --- /dev/null +++ b/docs/URLTextParameter.md @@ -0,0 +1,111 @@ +# URL Text 参数功能说明 + +## 功能概述 + +实现了从 URL 参数中获取 `text` 并自动填充到聊天输入框的功能。用户点击对话建议时,会跳转到聊天页面并自动填充对应的文本。 + +## 实现细节 + +### 1. StartChatItem 组件 + +**文件位置**: `src/app/(main)/home/components/StartChat/StartChatItem.tsx` + +**功能**: +- 对话建议列表中的每一项都是一个链接 +- 点击时跳转到聊天页面,并将建议文本作为 URL 参数传递 +- 使用 `encodeURIComponent()` 对文本进行编码,确保特殊字符正确传递 + +**示例**: +```tsx + + {suggestion} + +``` + +### 2. ChatInput 组件 + +**文件位置**: `src/app/(main)/chat/[aiId]/components/ChatMessageAction/ChatInput.tsx` + +**功能**: +- 使用 `useSearchParams()` Hook 获取 URL 参数 +- 在组件挂载时检查是否有 `text` 参数 +- 如果有,自动填充到输入框并聚焦 + +**实现代码**: +```tsx +const searchParams = useSearchParams(); + +useEffect(() => { + const textFromUrl = searchParams.get('text'); + if (textFromUrl) { + setMessage(textFromUrl); + // 聚焦到输入框 + if (textareaRef.current) { + textareaRef.current.focus(); + } + } +}, [searchParams]); +``` + +## 使用场景 + +### 场景 1: 对话建议快捷回复 +用户在首页看到 AI 角色的对话建议,点击后: +1. 跳转到聊天页面 +2. 建议文本自动填充到输入框 +3. 用户可以直接发送或修改后发送 + +### 场景 2: 外部链接跳转 +可以通过外部链接直接跳转到聊天页面并预填充文本: +``` +https://your-domain.com/chat/123?text=Hello%20there! +``` + +## URL 参数格式 + +- **参数名**: `text` +- **编码方式**: URL 编码 (使用 `encodeURIComponent`) +- **示例**: + - 原始文本: `How is your day ?` + - 编码后: `How%20is%20your%20day%20%3F` + - 完整 URL: `/chat/123?text=How%20is%20your%20day%20%3F` + +## 注意事项 + +1. **URL 编码**: 必须使用 `encodeURIComponent()` 对文本进行编码,避免特殊字符导致 URL 解析错误 +2. **自动聚焦**: 填充文本后会自动聚焦到输入框,提升用户体验 +3. **单次触发**: URL 参数只在组件挂载或 `searchParams` 变化时读取一次 +4. **不影响现有功能**: 如果 URL 中没有 `text` 参数,输入框保持原有行为 + +## 扩展建议 + +### 可能的增强功能: + +1. **清除 URL 参数**: 填充文本后清除 URL 中的 `text` 参数,避免刷新页面时重复填充 + ```tsx + const router = useRouter(); + // 填充后清除参数 + router.replace(`/chat/${aiId}`, { scroll: false }); + ``` + +2. **支持多个参数**: 可以扩展支持其他参数,如 `image`、`voice` 等 + ``` + /chat/123?text=Hello&image=https://... + ``` + +3. **参数验证**: 添加文本长度限制和内容验证 + ```tsx + if (textFromUrl && textFromUrl.length <= 1000) { + setMessage(textFromUrl); + } + ``` + +## 相关文件 + +- `src/app/(main)/home/components/StartChat/StartChatItem.tsx` - 发起跳转的组件 +- `src/app/(main)/chat/[aiId]/components/ChatMessageAction/ChatInput.tsx` - 接收参数的组件 +- `src/app/(main)/home/context/AudioPlayerContext.tsx` - 音频播放上下文(相关功能) + diff --git a/docs/VoiceTTSIntegration.md b/docs/VoiceTTSIntegration.md new file mode 100644 index 0000000..3916542 --- /dev/null +++ b/docs/VoiceTTSIntegration.md @@ -0,0 +1,169 @@ +# 语音合成功能集成文档 + +## 概述 + +在 VoiceSelector 组件中集成了基于 `useFetchVoiceTtsV2` 接口的语音合成功能,实现了智能缓存、自动播放和错误回退机制。 + +## 核心功能 + +### 1. 智能缓存 +- 基于配置参数生成唯一哈希值作为缓存键 +- 相同配置的语音只生成一次,存储在内存中 +- 支持手动清除缓存 + +### 2. 参数映射 +- `tone` (音调) → `loudnessRate` (音量) +- `speed` (语速) → `speechRate` (语速) +- 参数范围:[-50, 100] + +### 3. 播放逻辑 +1. **优先级**:TTS 生成的语音 > 预设语音文件 +2. **错误回退**:TTS 失败时自动使用预设语音 +3. **状态管理**:生成中、播放中、已缓存等状态 + +## 文件结构 + +``` +src/ +├── hooks/ +│ ├── useVoiceTTS.ts # 语音合成核心 Hook +│ └── useCommon.ts # TTS 接口 Hook +├── components/ +│ └── features/ +│ └── VoiceTTSPlayer.tsx # 独立的语音播放器组件 +├── app/ +│ ├── (main)/create/components/Voice/ +│ │ └── VoiceSelector.tsx # 集成了 TTS 的语音选择器 +│ ├── test-voice-tts/ +│ │ └── page.tsx # TTS 功能测试页面 +│ └── test-voice-selector/ +│ └── page.tsx # VoiceSelector 测试页面 +└── docs/ + └── VoiceTTSIntegration.md # 本文档 +``` + +## 使用方法 + +### 基础用法 + +```tsx +import { useVoiceTTS } from '@/hooks/useVoiceTTS' + +function MyComponent() { + const { generateAndPlay, isPlaying, isGenerating } = useVoiceTTS() + + const config = { + text: '你好,这是测试语音', + voiceType: 'S_zh_xiaoxiao_emotion', + speechRate: 0, + loudnessRate: 0 + } + + return ( + + ) +} +``` + +### VoiceSelector 集成 + +```tsx +import VoiceSelector from '@/app/(main)/create/components/Voice/VoiceSelector' + +function CreateForm() { + const [voiceConfig, setVoiceConfig] = useState({ + tone: 0, // 音调 [-50, 100] + speed: 0, // 语速 [-50, 100] + content: 'voice_id' // 语音类型ID + }) + + return ( + + ) +} +``` + +## 接口说明 + +### useFetchVoiceTtsV2 参数 + +```typescript +interface FetchVoiceTtsV2Request { + text?: string // 文本内容 + voiceType?: string // 语音类型 (以 S_ 开头) + speechRate?: number // 语速 [-50, 100] + loudnessRate?: number // 音量 [-50, 100] +} +``` + +### useVoiceTTS 选项 + +```typescript +interface UseVoiceTTSOptions { + autoPlay?: boolean // 生成完成后自动播放,默认 true + cacheEnabled?: boolean // 启用缓存,默认 true + onPlayStart?: (configHash: string) => void + onPlayEnd?: (configHash: string) => void + onGenerateStart?: (config: FetchVoiceTtsV2Request) => void + onGenerateEnd?: (config: FetchVoiceTtsV2Request, url: string) => void + onError?: (error: string) => void +} +``` + +## 状态说明 + +### VoiceSelector 状态 + +- **未选择**:显示提示选择语音 +- **已选择 + 未生成**:显示播放按钮 +- **生成中**:显示动画和 "Generating..." +- **播放中**:显示播放动画 +- **已缓存**:显示 "Cached" 标识 +- **错误**:显示错误信息 + +### 播放优先级 + +1. TTS 生成的语音(如果有有效的 voiceType 和 voiceText) +2. 预设语音文件(回退机制) +3. 错误处理和用户提示 + +## 缓存机制 + +- **缓存键**:基于 `text + voiceType + speechRate + loudnessRate` 生成哈希 +- **存储方式**:内存中存储(页面刷新后清除) +- **清除策略**:手动清除或组件卸载时清除 + +## 错误处理 + +1. **TTS 接口错误**:自动回退到预设语音 +2. **播放错误**:显示错误信息给用户 +3. **网络错误**:重试机制(在 useVoiceTTS 中实现) + +## 测试页面 + +- `/test-voice-tts` - 独立的 TTS 功能测试 +- `/test-voice-selector` - VoiceSelector 组件测试 + +## 性能优化 + +1. **缓存机制**:避免重复生成相同配置的语音 +2. **懒加载**:只在用户点击播放时才生成语音 +3. **错误回退**:TTS 失败时使用预设语音,不影响用户体验 +4. **状态管理**:精确的状态控制,避免不必要的重渲染 + +## 扩展功能 + +- 支持自定义缓存策略 +- 支持批量预生成常用语音 +- 支持语音质量选择 +- 支持播放进度控制 diff --git a/docs/copy-audit.xlsx b/docs/copy-audit.xlsx new file mode 100644 index 0000000..323b1af Binary files /dev/null and b/docs/copy-audit.xlsx differ diff --git a/docs/i18n-scan-report.json b/docs/i18n-scan-report.json new file mode 100644 index 0000000..320d93f --- /dev/null +++ b/docs/i18n-scan-report.json @@ -0,0 +1,77 @@ +{ + "totalItems": 2887, + "uniqueTexts": 860, + "translationKeys": 849, + "byRoute": { + "/": 107, + "shared": 385, + "debug-mock": 25, + "demo": 154, + "server-device-test": 130, + "test-avatar-crop": 79, + "test-avatar-setting": 5, + "test-discord": 141, + "test-image-crop": 41, + "test-lamejs": 12, + "test-middleware": 16, + "test-mp3-conversion": 35, + "test-s3-upload": 8, + "(auth)/about": 6, + "(auth)/login": 26, + "(main)/chat": 1, + "(main)/contact": 21, + "(main)/creator": 11, + "(main)/crushcoin": 13, + "(main)/explore": 3, + "(main)/leaderboard": 18, + "(main)/profile": 81, + "(main)/test-voice-wave": 70, + "(main)/vip": 19, + "(main)/wallet": 39, + "(auth)/login/fields": 33, + "(auth)/policy/privacy": 123, + "(auth)/policy/recharge": 159, + "(auth)/policy/tos": 131, + "(auth)/share/[userId]": 25, + "(main)/chat/[aiId]": 341, + "(main)/create": 205, + "(main)/generate/image": 23, + "(main)/generate/image-2-background": 26, + "(main)/generate/image-2-image": 25, + "(main)/generate/image-edit": 25, + "(main)/profile/account": 24, + "(main)/profile/edit": 34, + "(main)/user/[userId]": 78, + "(main)/wallet/transactions": 3, + "(main)/edit/[aiId]/character": 39, + "(main)/edit/[aiId]": 16, + "(main)/edit/[aiId]/dialogue": 52, + "(main)/edit/[aiId]/image": 44, + "(main)/edit/[aiId]/type": 25, + "(main)/wallet/charge/result": 10 + }, + "byKind": { + "title": 12, + "text": 2687, + "toast": 37, + "alt": 68, + "validation": 35, + "placeholder": 37, + "aria": 3, + "error": 5, + "label": 2, + "dialog": 1 + }, + "sampleKeys": [ + "_.notfound.title.empty_title", + "common.mockprovider.text.div", + "common.mockprovider.text.p", + "common.uselogout.toast.toast_success", + "common.useregister.toast.toast_success", + "common.useregister.toast.toast_error", + "debug_mock.debugmockpage.text.div", + "debug_mock.debugmockpage.text.h1", + "debug_mock.debugmockpage.text.h2", + "debug_mock.debugmockpage.text.button" + ] +} \ No newline at end of file diff --git a/docs/i18n-scan-report.xlsx b/docs/i18n-scan-report.xlsx new file mode 100644 index 0000000..8ab0944 Binary files /dev/null and b/docs/i18n-scan-report.xlsx differ diff --git a/docs/project-overview.md b/docs/project-overview.md new file mode 100644 index 0000000..ceb0918 --- /dev/null +++ b/docs/project-overview.md @@ -0,0 +1,66 @@ +# 项目概述 +这是一个使用 Next.js App Router 的 Web 应用. + +crushlevel-next/ +├── app/ +│ ├── (auth)/ # 路由组:认证相关页面 +│ │ ├── login/ +│ │ │ └── page.tsx # /login 页面 +│ │ ├── register/ +│ │ │ └── page.tsx # /register 页面 +│ │ └── layout.tsx # 认证页面共享布局 +│ ├── (dashboard)/ # 路由组:仪表盘相关页面 +│ │ ├── page.tsx # /dashboard 页面 +│ │ ├── settings/ +│ │ │ └── page.tsx # /dashboard/settings 页面 +│ │ └── layout.tsx # 仪表盘页面共享布局 +│ ├── api/ # API 路由 +│ │ ├── auth/ +│ │ │ └── route.ts # /api/auth 路由 +│ │ └── users/ +│ │ └── route.ts # /api/users 路由 +│ ├── layout.tsx # 根布局(全局布局) +│ ├── page.tsx # 根页面(/ 路由) +│ ├── globals.css # 全局样式 +│ ├── favicon.ico # 网站图标 +│ └── not-found.tsx # 404 页面 +├── components/ # 可复用组件 +│ ├── ui/ # UI 组件(如按钮、卡片等) +│ │ ├── Button.tsx +│ │ └── Card.tsx +│ ├── layout/ # 布局相关组件 +│ │ ├── Navbar.tsx +│ │ └── Footer.tsx +│ └── features/ # 功能相关组件 +│ ├── AuthForm.tsx +│ └── DashboardChart.tsx +├── lib/ # 工具函数和库 +│ ├── api.ts # API 调用封装 +│ ├── auth.ts # 认证相关逻辑 +│ └── db/ # 数据库连接和查询 +│ ├── prisma.ts +│ └── models.ts +├── types/ # TypeScript 类型定义 +│ ├── user.ts +│ └── post.ts +├── public/ # 静态资源 +│ ├── images/ +│ └── fonts/ +├── styles/ # 样式文件(如果不使用 globals.css) +│ ├── tailwind.css +│ └── components/ +├── middleware.ts # 中间件(如认证、国际化) +├── next.config.mjs # Next.js 配置文件 +├── tsconfig.json # TypeScript 配置文件 +├── package.json +├── docs # 文档 +└── README.md + + +## UI库 +使用Shadcn/U作为UI的基础组件,结合tailwindcss实现。 +token都存放在global.css中。 + +## 组件库 +基础组件库components/ui + diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..c85fb67 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,16 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), +]; + +export default eslintConfig; diff --git a/i18next-scanner.config.js b/i18next-scanner.config.js new file mode 100644 index 0000000..eb41591 --- /dev/null +++ b/i18next-scanner.config.js @@ -0,0 +1,70 @@ +module.exports = { + input: [ + 'src/**/*.{ts,tsx}', + '!src/**/*.d.ts', + '!src/**/node_modules/**', + '!src/**/.next/**', + '!src/**/__tests__/**', + '!src/**/mocks/**', + '!src/**/mock/**', + ], + output: './', + options: { + debug: true, + // 禁用默认的 i18next 函数扫描,因为项目还没有使用 i18next + func: { + list: [], + extensions: [] + }, + trans: { + component: 'Trans', + i18nKey: 'i18nKey', + defaultsKey: 'defaults', + extensions: ['.ts', '.tsx'], + acorn: { + ecmaVersion: 2020, + sourceType: 'module', + plugins: ['typescript', 'jsx'] + }, + fallbackKey: function(ns, value) { + return value; + } + }, + lngs: ['en'], + defaultLng: 'en', + defaultNs: 'translation', + defaultValue: function(lng, ns, key) { + return key; + }, + resource: { + loadPath: 'public/locales/{{lng}}/{{ns}}.json', + savePath: 'public/locales/{{lng}}/{{ns}}.json', + jsonIndent: 2, + lineEnding: '\n' + }, + nsSeparator: ':', + keySeparator: '.', + interpolation: { + prefix: '{{', + suffix: '}}' + }, + // 自定义提取规则,用于扫描未使用 i18next 的文本 + customTransComponents: [ + { + name: 'Trans', + props: ['i18nKey', 'defaults'] + } + ], + // 扫描 JSX 文本和属性 + detect: { + // 扫描 JSX 子元素文本 + jsxText: true, + // 扫描属性值 + attr: ['placeholder', 'title', 'alt', 'aria-label', 'aria-placeholder'], + // 扫描函数调用中的字符串 + func: ['toast', 'alert', 'confirm', 'message', 'console.log', 'console.error'], + // 扫描对象字面量中的 message 属性 + object: ['message', 'error', 'warning', 'success'] + } + } +}; diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..a13fb82 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,34 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ + eslint: { + ignoreDuringBuilds: true, + }, + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "picsum.photos", + }, + { + protocol: "https", + hostname: "public-pictures.epal.gg", + }, + { + protocol: "https", + hostname: "hhb.crushlevel.ai", + }, + { + protocol: "https", + hostname: "sub.crushlevel.ai", + }, + { + protocol: "https", + hostname: "img.crushlevel.ai", + } + ], + }, +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b0989b0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12302 @@ +{ + "name": "crushlevel-next", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "crushlevel-next", + "version": "0.1.0", + "dependencies": { + "@aws-sdk/client-s3": "^3.850.0", + "@aws-sdk/s3-request-presigner": "^3.859.0", + "@bprogress/next": "^3.2.12", + "@byteplus/rtc": "^4.67.4", + "@egjs/flicking-plugins": "^4.7.1", + "@egjs/react-flicking": "^4.14.3", + "@hookform/resolvers": "^5.1.1", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-aspect-ratio": "^1.1.7", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-tooltip": "^1.2.7", + "@tanstack/react-query": "^5.83.0", + "@tanstack/react-query-devtools": "^5.83.0", + "@types/crypto-js": "^4.2.2", + "@types/react-stickynode": "^4.0.3", + "axios": "^1.10.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "crypto-js": "^4.2.0", + "dayjs": "^1.11.13", + "decimal.js": "^10.6.0", + "embla-carousel-react": "^8.6.0", + "jotai": "^2.13.1", + "js-cookie": "^3.0.5", + "keen-slider": "^6.8.6", + "lamejs": "^1.2.1", + "lucide-react": "^0.525.0", + "next": "15.3.5", + "next-themes": "^0.4.6", + "nim-web-sdk-ng": "^10.9.41", + "numeral": "^2.0.6", + "qs": "^6.14.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-easy-crop": "^5.5.0", + "react-hook-form": "^7.60.0", + "react-photo-view": "^1.2.7", + "react-stickynode": "^5.0.2", + "sonner": "^2.0.6", + "swiper": "^12.0.3", + "tailwind-merge": "^3.3.1", + "vaul": "^1.1.2", + "zod": "^4.0.5" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/js-cookie": "^3.0.6", + "@types/node": "^20", + "@types/numeral": "^2.0.5", + "@types/qs": "^6.14.0", + "@types/react": "^19", + "@types/react-dom": "^19", + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "acorn-typescript": "^1.4.13", + "eslint": "^9", + "eslint-config-next": "15.3.5", + "globby": "^15.0.0", + "i18next-scanner": "^4.6.0", + "msw": "^2.10.4", + "tailwindcss": "^4", + "ts-morph": "^27.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.20.6", + "tw-animate-css": "^1.3.5", + "typescript": "^5", + "xlsx": "^0.18.5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.850.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.850.0.tgz", + "integrity": "sha512-tX5bUfqiLOh6jtAlaiAuOUKFYh8KDG9k9zFLUdgGplC5TP47AYTreUEg+deCTHo4DD3YCvrLuyZ8tIDgKu7neQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/credential-provider-node": "3.848.0", + "@aws-sdk/middleware-bucket-endpoint": "3.840.0", + "@aws-sdk/middleware-expect-continue": "3.840.0", + "@aws-sdk/middleware-flexible-checksums": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-location-constraint": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-sdk-s3": "3.846.0", + "@aws-sdk/middleware-ssec": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/signature-v4-multi-region": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/eventstream-serde-browser": "^4.0.4", + "@smithy/eventstream-serde-config-resolver": "^4.1.2", + "@smithy/eventstream-serde-node": "^4.0.4", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-blob-browser": "^4.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-stream": "^4.2.3", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.6", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.848.0.tgz", + "integrity": "sha512-mD+gOwoeZQvbecVLGoCmY6pS7kg02BHesbtIxUj+PeBqYoZV5uLvjUOmuGfw1SfoSobKvS11urxC9S7zxU/Maw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.846.0.tgz", + "integrity": "sha512-7CX0pM906r4WSS68fCTNMTtBCSkTtf3Wggssmx13gD40gcWEZXsU00KzPp1bYheNRyPlAq3rE22xt4wLPXbuxA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.7.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.846.0.tgz", + "integrity": "sha512-QuCQZET9enja7AWVISY+mpFrEIeHzvkx/JEEbHYzHhUkxcnC2Kq2c0bB7hDihGD0AZd3Xsm653hk1O97qu69zg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.846.0.tgz", + "integrity": "sha512-Jh1iKUuepdmtreMYozV2ePsPcOF5W9p3U4tWhi3v6nDvz0GsBjzjAROW+BW8XMz9vAD3I9R+8VC3/aq63p5nlw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.848.0.tgz", + "integrity": "sha512-r6KWOG+En2xujuMhgZu7dzOZV3/M5U/5+PXrG8dLQ3rdPRB3vgp5tc56KMqLwm/EXKRzAOSuw/UE4HfNOAB8Hw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/credential-provider-env": "3.846.0", + "@aws-sdk/credential-provider-http": "3.846.0", + "@aws-sdk/credential-provider-process": "3.846.0", + "@aws-sdk/credential-provider-sso": "3.848.0", + "@aws-sdk/credential-provider-web-identity": "3.848.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.848.0.tgz", + "integrity": "sha512-AblNesOqdzrfyASBCo1xW3uweiSro4Kft9/htdxLeCVU1KVOnFWA5P937MNahViRmIQm2sPBCqL8ZG0u9lnh5g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.846.0", + "@aws-sdk/credential-provider-http": "3.846.0", + "@aws-sdk/credential-provider-ini": "3.848.0", + "@aws-sdk/credential-provider-process": "3.846.0", + "@aws-sdk/credential-provider-sso": "3.848.0", + "@aws-sdk/credential-provider-web-identity": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.846.0.tgz", + "integrity": "sha512-mEpwDYarJSH+CIXnnHN0QOe0MXI+HuPStD6gsv3z/7Q6ESl8KRWon3weFZCDnqpiJMUVavlDR0PPlAFg2MQoPg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.848.0.tgz", + "integrity": "sha512-pozlDXOwJZL0e7w+dqXLgzVDB7oCx4WvtY0sk6l4i07uFliWF/exupb6pIehFWvTUcOvn5aFTTqcQaEzAD5Wsg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.848.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/token-providers": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.848.0.tgz", + "integrity": "sha512-D1fRpwPxtVDhcSc/D71exa2gYweV+ocp4D3brF0PgFd//JR3XahZ9W24rVnTQwYEcK9auiBZB89Ltv+WbWN8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.840.0.tgz", + "integrity": "sha512-+gkQNtPwcSMmlwBHFd4saVVS11In6ID1HczNzpM3MXKXRBfSlbZJbCt6wN//AZ8HMklZEik4tcEOG0qa9UY8SQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.840.0.tgz", + "integrity": "sha512-iJg2r6FKsKKvdiU4oCOuCf7Ro/YE0Q2BT/QyEZN3/Rt8Nr4SAZiQOlcBXOCpGvuIKOEAhvDOUnW3aDHL01PdVw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.846.0.tgz", + "integrity": "sha512-CdkeVfkwt3+bDLhmOwBxvkUf6oY9iUhvosaUnqkoPsOqIiUEN54yTGOnO8A0wLz6mMsZ6aBlfFrQhFnxt3c+yw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", + "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.840.0.tgz", + "integrity": "sha512-KVLD0u0YMF3aQkVF8bdyHAGWSUY6N1Du89htTLgqCcIhSxxAJ9qifrosVZ9jkAzqRW99hcufyt2LylcVU2yoKQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", + "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", + "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.846.0.tgz", + "integrity": "sha512-jP9x+2Q87J5l8FOP+jlAd7vGLn0cC6G9QGmf386e5OslBPqxXKcl3RjqGLIOKKos2mVItY3ApP5xdXQx7jGTVA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/core": "^3.7.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.840.0.tgz", + "integrity": "sha512-CBZP9t1QbjDFGOrtnUEHL1oAvmnCUUm7p0aPNbIdSzNtH42TNKjPRN3TuEIJDGjkrqpL3MXyDSmNayDcw/XW7Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.848.0.tgz", + "integrity": "sha512-rjMuqSWJEf169/ByxvBqfdei1iaduAnfolTshsZxwcmLIUtbYrFUmts0HrLQqsAG8feGPpDLHA272oPl+NTCCA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@smithy/core": "^3.7.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.848.0.tgz", + "integrity": "sha512-joLsyyo9u61jnZuyYzo1z7kmS7VgWRAkzSGESVzQHfOA1H2PYeUFek6vLT4+c9xMGrX/Z6B0tkRdzfdOPiatLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", + "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.859.0", + "resolved": "https://registry.npmmirror.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.859.0.tgz", + "integrity": "sha512-YpMv8I0h27ua74j+hVmsRQn+mDz/8Gb75i8KED3rYgrpoeob9xKlx4JdDaMVHHdFa8entoV7moI8uRrQxPD8Zw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-format-url": "3.840.0", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/core": { + "version": "3.858.0", + "resolved": "https://registry.npmmirror.com/@aws-sdk/core/-/core-3.858.0.tgz", + "integrity": "sha512-iWm4QLAS+/XMlnecIU1Y33qbBr1Ju+pmWam3xVCPlY4CSptKpVY+2hXOnmg9SbHAX9C005fWhrIn51oDd00c9A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.7.2", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.858.0", + "resolved": "https://registry.npmmirror.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.858.0.tgz", + "integrity": "sha512-g1LBHK9iAAMnh4rRX4/cGBuICH5R9boHUw4X9FkMC+ROAH9z1A2uy6bE55sg5guheAmVTQ5sOsVZb8QPEQbIUA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/core": "^3.7.2", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.858.0", + "resolved": "https://registry.npmmirror.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.858.0.tgz", + "integrity": "sha512-WtQvCtIz8KzTqd/OhjziWb5nAFDEZ0pE1KJsWBZ0j6Ngvp17ORSY37U96buU0SlNNflloGT7ZIlDkdFh73YktA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.846.0.tgz", + "integrity": "sha512-ZMfIMxUljqZzPJGOcraC6erwq/z1puNMU35cO1a/WdhB+LdYknMn1lr7SJuH754QwNzzIlZbEgg4hoHw50+DpQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.848.0.tgz", + "integrity": "sha512-oNPyM4+Di2Umu0JJRFSxDcKQ35+Chl/rAwD47/bS0cDPI8yrao83mLXLeDqpRPHyQW4sXlP763FZcuAibC0+mg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", + "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.848.0.tgz", + "integrity": "sha512-fY/NuFFCq/78liHvRyFKr+aqq1aA/uuVSANjzr5Ym8c+9Z3HRPE9OrExAHoMrZ6zC8tHerQwlsXYYH5XZ7H+ww==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.840.0.tgz", + "integrity": "sha512-VB1PWyI1TQPiPvg4w7tgUGGQER1xxXPNUqfh3baxUSFi1Oh8wHrDnFywkxLm3NMmgDmnLnSZ5Q326qAoyqKLSg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", + "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", + "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.848.0.tgz", + "integrity": "sha512-Zz1ft9NiLqbzNj/M0jVNxaoxI2F4tGXN0ZbZIj+KJ+PbJo+w5+Jo6d0UDAtbj3AEd79pjcCaP4OA9NTVzItUdw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bprogress/core": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@bprogress/core/-/core-1.3.4.tgz", + "integrity": "sha512-q/AqpurI/1uJzOrQROuZWixn/+ARekh+uvJGwLCP6HQ/EqAX4SkvNf618tSBxL4NysC0MwqAppb/mRw6Tzi61w==", + "license": "MIT" + }, + "node_modules/@bprogress/next": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/@bprogress/next/-/next-3.2.12.tgz", + "integrity": "sha512-/ZvNwbAd0ty9QiQwCfT2AfwWVdAaEyCPx5RUz3CfiiJS/OLBohhDz/IC/srhwK9GnXeXavvtiUrpKzN5GJDwlw==", + "license": "MIT", + "dependencies": { + "@bprogress/core": "^1.3.4", + "@bprogress/react": "^1.2.7" + }, + "peerDependencies": { + "next": ">=13.0.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@bprogress/react": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@bprogress/react/-/react-1.2.7.tgz", + "integrity": "sha512-MqJfHW+R5CQeWqyqrLxUjdBRHk24Xl63OkBLo5DMWqUqocUikRTfCIc/jtQQbPk7BRfdr5OP3Lx7YlfQ9QOZMQ==", + "license": "MIT", + "dependencies": { + "@bprogress/core": "^1.3.4" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.7.2" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, + "node_modules/@byteplus/rtc": { + "version": "4.67.4", + "resolved": "https://registry.npmjs.org/@byteplus/rtc/-/rtc-4.67.4.tgz", + "integrity": "sha512-UABVde+HBJtYlax4VZ1+iZtxG2+mbAA2HakwOkdCafpc4ltqWcCnLW/2HHF00nMGG36FxB8ho6sweiECBI9iTQ==", + "license": "BSD-3-Clause", + "dependencies": { + "eventemitter3": "^4.0.7" + } + }, + "node_modules/@cfcs/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@cfcs/core/-/core-0.1.0.tgz", + "integrity": "sha512-kvYX0RpL45XTHJ5sW7teNbKeAa7pK3nNqaJPoFfZDPTIBJOkTtRD3QhkBG+O3Hu69a8xeMoPvF6y/RtJ6JUOdA==", + "license": "MIT", + "dependencies": { + "@egjs/component": "^3.0.4" + } + }, + "node_modules/@cfcs/react": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@cfcs/react/-/react-0.1.0.tgz", + "integrity": "sha512-3IaDaE1Qn0iLZ1ZqD0cqoiX0Kjzp6/VB56409A963lQ5kK6ONNa3cNbCBqFZccCfPSnyzeSdvPlg75Af3W4raA==", + "license": "MIT", + "dependencies": { + "@cfcs/core": "~0.1.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@egjs/agent": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@egjs/agent/-/agent-2.4.4.tgz", + "integrity": "sha512-cvAPSlUILhBBOakn2krdPnOGv5hAZq92f1YHxYcfu0p7uarix2C6Ia3AVizpS1SGRZGiEkIS5E+IVTLg1I2Iog==", + "license": "MIT" + }, + "node_modules/@egjs/axes": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@egjs/axes/-/axes-3.9.2.tgz", + "integrity": "sha512-LEyJfhAdOrg672twCtH9KUloMTGCERm2tqxNLaXFjm/2rOcd4n5yrUofIjY0m/oG146XmSLeCR9+ri2kOA4BHg==", + "license": "MIT", + "dependencies": { + "@cfcs/core": "^0.1.0", + "@egjs/agent": "^2.2.1", + "@egjs/component": "^3.0.1" + } + }, + "node_modules/@egjs/component": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@egjs/component/-/component-3.0.5.tgz", + "integrity": "sha512-cLcGizTrrUNA2EYE3MBmEDt2tQv1joVP1Q3oDisZ5nw0MZDx2kcgEXM+/kZpfa/PAkFvYVhRUZwytIQWoN3V/w==", + "license": "MIT" + }, + "node_modules/@egjs/flicking": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@egjs/flicking/-/flicking-4.14.1.tgz", + "integrity": "sha512-6wmQhIwp1hjFYsJCf3mOfQZHaZRE4F9EUtNR/s+U8CI9360tHPPe/SZzohiZGXvNNw0J85iuUM24wO+2oMGo+w==", + "license": "MIT", + "dependencies": { + "@cfcs/core": "^0.1.0", + "@egjs/axes": "^3.9.1", + "@egjs/component": "^3.0.1", + "@egjs/imready": "^1.3.1", + "@egjs/list-differ": "^1.0.1" + } + }, + "node_modules/@egjs/flicking-plugins": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@egjs/flicking-plugins/-/flicking-plugins-4.7.1.tgz", + "integrity": "sha512-J0k499sCs5IAMXAJuBcrY1OoiHRi4BPbS1/RCCkIzsYgdoHzcAUjKyhSsms3gLz4O/K5eTbBmWBUtFO86fETHg==", + "license": "MIT", + "peerDependencies": { + "@egjs/flicking": "^4.1.0" + } + }, + "node_modules/@egjs/imready": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@egjs/imready/-/imready-1.4.2.tgz", + "integrity": "sha512-ckV7KlBa23Bw6iZP59W2cMZfAmV5q0BtMoOz7ZkzIvjoOtkxfLZkqVJROSgDP5Ha0hsnskyL4vtom9Bt7FZSRQ==", + "license": "MIT", + "dependencies": { + "@cfcs/core": "^0.0.24", + "@egjs/component": "^3.0.1" + } + }, + "node_modules/@egjs/imready/node_modules/@cfcs/core": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/@cfcs/core/-/core-0.0.24.tgz", + "integrity": "sha512-feB38qu+eDk0Pggh/yR7gjaNmvUYA2uCxHP3Pz2MLE4LZ/9jPdtu8bzCSI47yTEhWyZCF5Pk698hdz8IN2mTjA==", + "license": "MIT", + "dependencies": { + "@egjs/component": "^3.0.4" + } + }, + "node_modules/@egjs/list-differ": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@egjs/list-differ/-/list-differ-1.0.1.tgz", + "integrity": "sha512-OTFTDQcWS+1ZREOdCWuk5hCBgYO4OsD30lXcOCyVOAjXMhgL5rBRDnt/otb6Nz8CzU0L/igdcaQBDLWc4t9gvg==", + "license": "MIT" + }, + "node_modules/@egjs/react-flicking": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@egjs/react-flicking/-/react-flicking-4.14.3.tgz", + "integrity": "sha512-CULlGGGfLKxOuX9viuozUR4bHVFbfhRDAyfpCrpd28MpwDtRYaYtRongpYhlDUk6L3DbYmuGbNPIq9WbZ96UyQ==", + "license": "MIT", + "dependencies": { + "@cfcs/core": "^0.1.0", + "@cfcs/react": "^0.1.0", + "@egjs/component": "^3.0.2", + "@egjs/flicking": "~4.14.1", + "@egjs/list-differ": "^1.0.1" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", + "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", + "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", + "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.4.tgz", + "integrity": "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.2" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@gulpjs/to-absolute-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz", + "integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.1.1.tgz", + "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.4" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.13.tgz", + "integrity": "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.14.tgz", + "integrity": "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.39.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.3.tgz", + "integrity": "sha512-9bw/wBL7pblsnOCIqvn1788S9o4h+cC5HWXg0Xhh0dOzsZ53IyfmBM+FYqpDDPbm0xjCqEqvCITloF3Dm4TXRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.5.tgz", + "integrity": "sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.5.tgz", + "integrity": "sha512-BZwWPGfp9po/rAnJcwUBaM+yT/+yTWIkWdyDwc74G9jcfTrNrmsHe+hXHljV066YNdVs8cxROxX5IgMQGX190w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.5.tgz", + "integrity": "sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.5.tgz", + "integrity": "sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.5.tgz", + "integrity": "sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.5.tgz", + "integrity": "sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.5.tgz", + "integrity": "sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.5.tgz", + "integrity": "sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.5.tgz", + "integrity": "sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.5.tgz", + "integrity": "sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.14.tgz", + "integrity": "sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.14", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-aspect-ratio": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.7.tgz", + "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.7.tgz", + "integrity": "sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz", + "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz", + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", + "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.7.2.tgz", + "integrity": "sha512-JoLw59sT5Bm8SAjFCYZyuCGxK8y3vovmoVbZWLDPTH5XpPEIwpFd9m90jjVMwoypDuB/SdVgje5Y4T7w50lJaw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.4.tgz", + "integrity": "sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.4.tgz", + "integrity": "sha512-3fb/9SYaYqbpy/z/H3yIi0bYKyAa89y6xPmIqwr2vQiUT2St+avRt8UKwsWt9fEdEasc5d/V+QjrviRaX1JRFA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.2.tgz", + "integrity": "sha512-JGtambizrWP50xHgbzZI04IWU7LdI0nh/wGbqH3sJesYToMi2j/DcoElqyOcqEIG/D4tNyxgRuaqBXWE3zOFhQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.4.tgz", + "integrity": "sha512-RD6UwNZ5zISpOWPuhVgRz60GkSIp0dy1fuZmj4RYmqLVRtejFqQ16WmfYDdoSoAjlp1LX+FnZo+/hkdmyyGZ1w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.4.tgz", + "integrity": "sha512-UeJpOmLGhq1SLox79QWw/0n2PFX+oPRE1ZyRMxPIaFEfCqWaqpB7BU9C8kpPOGEhLF7AwEqfFbtwNxGy4ReENA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.0.tgz", + "integrity": "sha512-mADw7MS0bYe2OGKkHYMaqarOXuDwRbO6ArD91XhHcl2ynjGCFF+hvqf0LyQcYxkA1zaWjefSkU7Ne9mqgApSgQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.4.tgz", + "integrity": "sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.4.tgz", + "integrity": "sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.4.tgz", + "integrity": "sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.17.tgz", + "integrity": "sha512-S3hSGLKmHG1m35p/MObQCBCdRsrpbPU8B129BVzRqRfDvQqPMQ14iO4LyRw+7LNizYc605COYAcjqgawqi+6jA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.7.2", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.18.tgz", + "integrity": "sha512-bYLZ4DkoxSsPxpdmeapvAKy7rM5+25gR7PGxq2iMiecmbrRGBHj9s75N74Ylg+aBiw9i5jIowC/cLU2NR0qH8w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.6", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz", + "integrity": "sha512-vqfSiHz2v8b3TTTrdXi03vNz1KLYYS3bhHCDv36FYDqxT7jvTll1mMnCrkD+gOvgwybuunh/2VmvOMqwBegxEg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", + "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.9.tgz", + "integrity": "sha512-mbMg8mIUAWwMmb74LoYiArP04zWElPzDoA1jVOp3or0cjlDMgoS6WTC3QXK0Vxoc9I4zdrX0tq6qsOmaIoTWEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.7.2", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.25.tgz", + "integrity": "sha512-pxEWsxIsOPLfKNXvpgFHBGFC3pKYKUFhrud1kyooO9CJai6aaKDHfT10Mi5iiipPXN/JhKAu3qX9o75+X85OdQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.25.tgz", + "integrity": "sha512-+w4n4hKFayeCyELZLfsSQG5mCC3TwSkmRHv4+el5CzFU8ToQpYGhpV7mrRzqlwKkntlPilT1HJy1TVeEvEjWOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", + "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.6", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.3.tgz", + "integrity": "sha512-cQn412DWHHFNKrQfbHY8vSFI3nTROY1aIKji9N0tpp8gUABRilr7wdf8fqBbSlXresobM+tQFNk6I+0LXK/YZg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.6.tgz", + "integrity": "sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "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.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", + "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "postcss": "^8.4.41", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.83.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz", + "integrity": "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.81.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.81.2.tgz", + "integrity": "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.83.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz", + "integrity": "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.83.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.83.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.83.0.tgz", + "integrity": "sha512-yfp8Uqd3I1jgx8gl0lxbSSESu5y4MO2ThOPBnGNTYs0P+ZFu+E9g5IdOngyUGuo6Uz6Qa7p9TLdZEX3ntik2fQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.81.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.83.0", + "react": "^18 || ^19" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", + "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.14" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.8.tgz", + "integrity": "sha512-HzbgCY53T6bfu4tT7Aq3TvViJyHjLjPNaAS3HOuMc9pw97KHsUtXNX4L+wu59g1WnjsZSko35MbEqnO58rihhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/numeral": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/numeral/-/numeral-2.0.5.tgz", + "integrity": "sha512-kH8I7OSSwQu9DS9JYdFWbuvhVzvFRoCPCkGxNwoGgaPeDfEPJlcxNvEOypZhQ3XXHsGbfIuYcxcJxKUfJHnRfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-stickynode": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/react-stickynode/-/react-stickynode-4.0.3.tgz", + "integrity": "sha512-K7YkwdhXQE4YVxIVweix4nkpdG4onm/dcnKK+qCj0vgUrNiKng+09zOfjF5AlOcC1HQkg5yxVLwp/0AzT84R0w==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.37.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.37.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-class-fields": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/acorn-class-fields/-/acorn-class-fields-0.3.7.tgz", + "integrity": "sha512-jdUWSFce0fuADUljmExz4TWpPkxmRW/ZCPRqeeUzbGf0vFUcpQYbyq52l75qGd0oSwwtAepeL6hgb/naRgvcKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn-private-class-elements": "^0.2.7" + }, + "engines": { + "node": ">=4.8.2" + }, + "peerDependencies": { + "acorn": "^6 || ^7 || ^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-private-class-elements": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/acorn-private-class-elements/-/acorn-private-class-elements-0.2.7.tgz", + "integrity": "sha512-+GZH2wOKNZOBI4OOPmzpo4cs6mW297sn6fgIk1dUI08jGjhAaEwvC39mN2gJAg2lmAQJ1rBkFqKWonL3Zz6PVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.8.2" + }, + "peerDependencies": { + "acorn": "^6.1.0 || ^7 || ^8" + } + }, + "node_modules/acorn-private-methods": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/acorn-private-methods/-/acorn-private-methods-0.3.3.tgz", + "integrity": "sha512-46oeEol3YFvLSah5m9hGMlNpxDBCEkdceJgf01AjqKYTK9r6HexKs2rgSbLK81pYjZZMonhftuUReGMlbbv05w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn-private-class-elements": "^0.2.7" + }, + "engines": { + "node": ">=4.8.2" + }, + "peerDependencies": { + "acorn": "^6 || ^7 || ^8" + } + }, + "node_modules/acorn-stage3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-stage3/-/acorn-stage3-4.0.0.tgz", + "integrity": "sha512-BR+LaADtA6GTB5prkNqWmlmCLYmkyW0whvSxdHhbupTaro2qBJ95fJDEiRLPUmiACGHPaYyeH9xmNJWdGfXRQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn-class-fields": "^0.3.7", + "acorn-private-methods": "^0.3.3", + "acorn-static-class-features": "^0.2.4" + }, + "engines": { + "node": ">=4.8.2" + }, + "peerDependencies": { + "acorn": "^7.4 || ^8" + } + }, + "node_modules/acorn-static-class-features": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/acorn-static-class-features/-/acorn-static-class-features-0.2.4.tgz", + "integrity": "sha512-5X4mpYq5J3pdndLmIB0+WtFd/mKWnNYpuTlTzj32wUu/PMmEGOiayQ5UrqgwdBNiaZBtDDh5kddpP7Yg2QaQYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn-private-class-elements": "^0.2.7" + }, + "engines": { + "node": ">=4.8.2" + }, + "peerDependencies": { + "acorn": "^6.1.0 || ^7 || ^8" + } + }, + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": ">=8.9.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.0.tgz", + "integrity": "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "license": "MIT" + }, + "node_modules/embla-carousel-react": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ensure-type": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ensure-type/-/ensure-type-1.5.1.tgz", + "integrity": "sha512-Dxe+mVF4MupV6eueWiFa6hUd9OL9lIM2/LqR40k1P+dwG+G2il2UigXTU9aQlaw+Y/N0BKSaTofNw73htTbC5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/eol": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz", + "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.5.tgz", + "integrity": "sha512-oQdvnIgP68wh2RlR3MdQpvaJ94R6qEFl+lnu8ZKxPj5fsAHrSF/HlAOZcsimLw3DT6bnEQIUdbZC2Ab6sWyptg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "15.3.5", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima-next": { + "version": "5.8.4", + "resolved": "https://registry.npmjs.org/esprima-next/-/esprima-next-5.8.4.tgz", + "integrity": "sha512-8nYVZ4ioIH4Msjb/XmhnBdz5WRRBaYqevKa1cv9nGJdCehMbzZCPNEEnqfLCZVetUVrUPEcb5IYyu1GG4hFqgg==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz", + "integrity": "sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.8", + "streamx": "^2.12.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-stream": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@gulpjs/to-absolute-glob": "^4.0.0", + "anymatch": "^3.1.3", + "fastq": "^1.13.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "is-negated-glob": "^1.0.0", + "normalize-path": "^3.0.0", + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-15.0.0.tgz", + "integrity": "sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/globby/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/graphql": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/gulp-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-sort/-/gulp-sort-2.0.0.tgz", + "integrity": "sha512-MyTel3FXOdh1qhw1yKhpimQrAmur9q1X0ZigLmCOxouQD+BD3za9/89O+HfbgBQvvh4igEbp0/PUWO+VqGYG1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.1" + } + }, + "node_modules/gulp-sort/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-sort/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-sort/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-sort/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-sort/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/i18next": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.0.tgz", + "integrity": "sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-scanner": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/i18next-scanner/-/i18next-scanner-4.6.0.tgz", + "integrity": "sha512-I/xKcwKfii3L3is3bUvfaIU0QA/wYhpZnjppfrzyb61QQddxkcpspASEtmfnxSYvE6yIaAxDlIxg0EHV7mxssg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.0.4", + "acorn-jsx": "^5.3.1", + "acorn-stage3": "^4.0.0", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "clone-deep": "^4.0.0", + "commander": "^9.0.0", + "deepmerge": "^4.0.0", + "ensure-type": "^1.5.0", + "eol": "^0.9.1", + "esprima-next": "^5.7.0", + "gulp-sort": "^2.0.0", + "i18next": "*", + "lodash": "^4.0.0", + "parse5": "^6.0.0", + "sortobject": "^4.0.0", + "through2": "^4.0.0", + "vinyl": "^3.0.0", + "vinyl-fs": "^4.0.0" + }, + "bin": { + "i18next-scanner": "bin/cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT", + "optional": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jotai": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.13.1.tgz", + "integrity": "sha512-cRsw6kFeGC9Z/D3egVKrTXRweycZ4z/k7i2MrfCzPYsL9SIWcPXTyqv258/+Ay8VUEcihNiE/coBLE6Kic6b8A==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0", + "@babel/template": ">=7.0.0", + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@babel/template": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keen-slider": { + "version": "6.8.6", + "resolved": "https://registry.npmjs.org/keen-slider/-/keen-slider-6.8.6.tgz", + "integrity": "sha512-dcEQ7GDBpCjUQA8XZeWh3oBBLLmyn8aoeIQFGL/NTVkoEOsmlnXqA4QykUm/SncolAZYGsEk/PfUhLZ7mwMM2w==", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lamejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/lamejs/-/lamejs-1.2.1.tgz", + "integrity": "sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==", + "license": "LGPL-3.0", + "dependencies": { + "use-strict": "1.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/lead": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-4.0.0.tgz", + "integrity": "sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "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-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lucide-react": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msw": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.4.tgz", + "integrity": "sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.39.1", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.26.1", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/napi-postinstall": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", + "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.5.tgz", + "integrity": "sha512-RkazLBMMDJSJ4XZQ81kolSpwiCt907l0xcgcpF4xC2Vml6QVcPNXW0NQRwQ80FFtSn7UM52XN0anaw8TEJXaiw==", + "license": "MIT", + "dependencies": { + "@next/env": "15.3.5", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.3.5", + "@next/swc-darwin-x64": "15.3.5", + "@next/swc-linux-arm64-gnu": "15.3.5", + "@next/swc-linux-arm64-musl": "15.3.5", + "@next/swc-linux-x64-gnu": "15.3.5", + "@next/swc-linux-x64-musl": "15.3.5", + "@next/swc-win32-arm64-msvc": "15.3.5", + "@next/swc-win32-x64-msvc": "15.3.5", + "sharp": "^0.34.1" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/nim-web-sdk-ng": { + "version": "10.9.41", + "resolved": "https://registry.npmjs.org/nim-web-sdk-ng/-/nim-web-sdk-ng-10.9.41.tgz", + "integrity": "sha512-wUvZeiuwlnOoLmDl9KkrpPbKhmsTpcOLAaKxCsF9apKdADejobIjs2+Im9VFHXfxTrQXVGswccz/W+PozGaGcA==", + "license": "ISC" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", + "license": "BSD-3-Clause" + }, + "node_modules/now-and-later": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", + "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "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": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-easy-crop": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.0.tgz", + "integrity": "sha512-OZzU+yXMhe69vLkDex+5QxcfT94FdcgVCyW2dBUw35ZoC3Is42TUxUy04w8nH1mfMKaizVdC3rh/wUfNW1mK4w==", + "license": "MIT", + "dependencies": { + "normalize-wheel": "^1.0.1", + "tslib": "^2.0.1" + }, + "peerDependencies": { + "react": ">=16.4.0", + "react-dom": ">=16.4.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.60.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.60.0.tgz", + "integrity": "sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-photo-view": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/react-photo-view/-/react-photo-view-1.2.7.tgz", + "integrity": "sha512-MfOWVPxuibncRLaycZUNxqYU8D9IA+rbGDDaq6GM8RIoGJal592hEJoRAyRSI7ZxyyJNJTLMUWWL3UIXHJJOpw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-stickynode": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/react-stickynode/-/react-stickynode-5.0.2.tgz", + "integrity": "sha512-aywk3qFTPu5VMKexra5VpDzQCRt3JkBk/Ft6SEzuXcafKz9sYal8QhY8Xfjt6BGmq2Teh90E/QcIfyzKFg9vuw==", + "license": "BSD-3-Clause", + "dependencies": { + "classnames": "^2.0.0", + "prop-types": "^15.6.0", + "shallowequal": "^1.0.0", + "subscribe-ui-event": "^3.0.0" + }, + "engines": { + "node": ">=16", + "npm": ">=8.4" + }, + "peerDependencies": { + "react": "^0.14.2 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.2 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", + "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "value-or-function": "^4.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, + "node_modules/sharp": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sonner": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.6.tgz", + "integrity": "sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/sortobject": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/sortobject/-/sortobject-4.17.0.tgz", + "integrity": "sha512-gzx7USv55AFRQ7UCWJHHauwD/ptUHF9MLXCGO3f5M9zauDPZ/4a9H6/VVbOXefdpEoI1unwB/bArHIVMbWBHmA==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-composer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", + "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.13.2" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/subscribe-ui-event": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/subscribe-ui-event/-/subscribe-ui-event-3.0.0.tgz", + "integrity": "sha512-Rgop/8yYQjgK47HsPKSxGLJnL/n2ejtAl0PAW9Bvt1kt+pEDN9SB3RP5JVUPjjuYZoFcGb3MOFNIf06uIzrazg==", + "license": "BSD-3-Clause", + "dependencies": { + "eventemitter3": "^5.0.0", + "raf": "^3.0.0" + } + }, + "node_modules/subscribe-ui-event/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swiper": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-12.0.3.tgz", + "integrity": "sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-through": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", + "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-morph": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", + "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.28.1", + "code-block-writer": "^13.0.3" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tw-animate-css": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.5.tgz", + "integrity": "sha512-t3u+0YNoloIhj1mMXs779P6MO9q3p3mvGn4k1n3nJPqJw/glZcuijG2qTSN4z4mgNRfW5ZC3aXJFLwDtiipZXA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-strict": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/use-strict/-/use-strict-1.0.1.tgz", + "integrity": "sha512-IeiWvvEXfW5ltKVMkxq6FvNf2LojMKvB2OCeja6+ct24S1XOmQw2dGr2JyndwACWAGJva9B7yPHwAmeA9QCqAQ==", + "license": "ISC" + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/value-or-function": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", + "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-contents": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", + "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^5.0.0", + "vinyl": "^3.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-fs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-mkdirp-stream": "^2.0.1", + "glob-stream": "^8.0.3", + "graceful-fs": "^4.2.11", + "iconv-lite": "^0.6.3", + "is-valid-glob": "^1.0.0", + "lead": "^4.0.0", + "normalize-path": "3.0.0", + "resolve-options": "^2.0.0", + "stream-composer": "^1.0.2", + "streamx": "^2.14.0", + "to-through": "^3.0.0", + "value-or-function": "^4.0.0", + "vinyl": "^3.0.1", + "vinyl-sourcemap": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", + "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-source-map": "^2.0.0", + "graceful-fs": "^4.2.10", + "now-and-later": "^3.0.0", + "streamx": "^2.12.5", + "vinyl": "^3.0.0", + "vinyl-contents": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz", + "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1da0f3c --- /dev/null +++ b/package.json @@ -0,0 +1,99 @@ +{ + "name": "crushlevel-next", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint", + "i18n:scan": "i18next-scanner", + "i18n:scan-custom": "tsx scripts/i18n-scan.ts", + "i18n:convert": "node scripts/convert-to-i18n.js" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.850.0", + "@aws-sdk/s3-request-presigner": "^3.859.0", + "@bprogress/next": "^3.2.12", + "@byteplus/rtc": "^4.67.4", + "@egjs/flicking-plugins": "^4.7.1", + "@egjs/react-flicking": "^4.14.3", + "@hookform/resolvers": "^5.1.1", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-aspect-ratio": "^1.1.7", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-tooltip": "^1.2.7", + "@tanstack/react-query": "^5.83.0", + "@tanstack/react-query-devtools": "^5.83.0", + "@types/crypto-js": "^4.2.2", + "@types/react-stickynode": "^4.0.3", + "axios": "^1.10.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "crypto-js": "^4.2.0", + "dayjs": "^1.11.13", + "decimal.js": "^10.6.0", + "embla-carousel-react": "^8.6.0", + "jotai": "^2.13.1", + "js-cookie": "^3.0.5", + "keen-slider": "^6.8.6", + "lamejs": "^1.2.1", + "lucide-react": "^0.525.0", + "next": "15.3.5", + "next-themes": "^0.4.6", + "nim-web-sdk-ng": "^10.9.41", + "numeral": "^2.0.6", + "qs": "^6.14.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-easy-crop": "^5.5.0", + "react-hook-form": "^7.60.0", + "react-photo-view": "^1.2.7", + "react-stickynode": "^5.0.2", + "sonner": "^2.0.6", + "swiper": "^12.0.3", + "tailwind-merge": "^3.3.1", + "vaul": "^1.1.2", + "zod": "^4.0.5" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/js-cookie": "^3.0.6", + "@types/node": "^20", + "@types/numeral": "^2.0.5", + "@types/qs": "^6.14.0", + "@types/react": "^19", + "@types/react-dom": "^19", + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "acorn-typescript": "^1.4.13", + "eslint": "^9", + "eslint-config-next": "15.3.5", + "globby": "^15.0.0", + "i18next-scanner": "^4.6.0", + "msw": "^2.10.4", + "tailwindcss": "^4", + "ts-morph": "^27.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.20.6", + "tw-animate-css": "^1.3.5", + "typescript": "^5", + "xlsx": "^0.18.5" + }, + "msw": { + "workerDirectory": [ + "public" + ] + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..c7bcb4b --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/public/common-bg.png b/public/common-bg.png new file mode 100644 index 0000000..7110460 Binary files /dev/null and b/public/common-bg.png differ diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/font/demo.css b/public/font/demo.css new file mode 100644 index 0000000..a67054a --- /dev/null +++ b/public/font/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/public/font/demo_index.html b/public/font/demo_index.html new file mode 100644 index 0000000..bc64c00 --- /dev/null +++ b/public/font/demo_index.html @@ -0,0 +1,4880 @@ + + + + + iconfont Demo + + + + + + + + + + + + + +
+

+ + +

+ +
+
+
    + +
  • + +
    shield
    +
    &#xe6fa;
    +
  • + +
  • + +
    audits
    +
    &#xe6fb;
    +
  • + +
  • + +
    post_recommend_fill
    +
    &#xe6f9;
    +
  • + +
  • + +
    post_Notrecommend_fill
    +
    &#xe6f8;
    +
  • + +
  • + +
    voice_msg
    +
    &#xe652;
    +
  • + +
  • + +
    clear
    +
    &#xe657;
    +
  • + +
  • + +
    prompt
    +
    &#xe66f;
    +
  • + +
  • + +
    statistics
    +
    &#xe66e;
    +
  • + +
  • + +
    sort_az
    +
    &#xe66d;
    +
  • + +
  • + +
    orderlobby
    +
    &#xe6f7;
    +
  • + +
  • + +
    link
    +
    &#xe6f6;
    +
  • + +
  • + +
    minimize
    +
    &#xe66c;
    +
  • + +
  • + +
    icon_watchtogether
    +
    &#xe6f5;
    +
  • + +
  • + +
    seat
    +
    &#xe6f4;
    +
  • + +
  • + +
    mute all
    +
    &#xe66b;
    +
  • + +
  • + +
    entrance sound
    +
    &#xe6f3;
    +
  • + +
  • + +
    sort
    +
    &#xe66a;
    +
  • + +
  • + +
    VIP_nocolor
    +
    &#xe6f2;
    +
  • + +
  • + +
    purchaseorder
    +
    &#xe6f0;
    +
  • + +
  • + +
    placeorder
    +
    &#xe6f1;
    +
  • + +
  • + +
    connect
    +
    &#xe669;
    +
  • + +
  • + +
    tag
    +
    &#xe668;
    +
  • + +
  • + +
    card
    +
    &#xe667;
    +
  • + +
  • + +
    Steam Deck
    +
    &#xe666;
    +
  • + +
  • + +
    Android
    +
    &#xe661;
    +
  • + +
  • + +
    Xbox
    +
    &#xe662;
    +
  • + +
  • + +
    PS
    +
    &#xe663;
    +
  • + +
  • + +
    Switch
    +
    &#xe664;
    +
  • + +
  • + +
    PC
    +
    &#xe665;
    +
  • + +
  • + +
    thread
    +
    &#xe6ef;
    +
  • + +
  • + +
    icon_post_notrecommend
    +
    &#xe6ee;
    +
  • + +
  • + +
    coupon-border
    +
    &#xe660;
    +
  • + +
  • + +
    date
    +
    &#xe6ed;
    +
  • + +
  • + +
    mic request border
    +
    &#xe65e;
    +
  • + +
  • + +
    mic request
    +
    &#xe65f;
    +
  • + +
  • + +
    icon-public-border
    +
    &#xe65c;
    +
  • + +
  • + +
    icon-private-border
    +
    &#xe65d;
    +
  • + +
  • + +
    female
    +
    &#xe659;
    +
  • + +
  • + +
    nonconforming
    +
    &#xe65a;
    +
  • + +
  • + +
    male
    +
    &#xe65b;
    +
  • + +
  • + +
    icon_exclamation
    +
    &#xe6ec;
    +
  • + +
  • + +
    stop
    +
    &#xe658;
    +
  • + +
  • + +
    language
    +
    &#xe654;
    +
  • + +
  • + +
    account
    +
    &#xe655;
    +
  • + +
  • + +
    IM-setting
    +
    &#xe656;
    +
  • + +
  • + +
    info
    +
    &#xe653;
    +
  • + +
  • + +
    copy
    +
    &#xe6eb;
    +
  • + +
  • + +
    icon-drag
    +
    &#xe650;
    +
  • + +
  • + +
    Invite to mic
    +
    &#xe6ea;
    +
  • + +
  • + +
    voice-live
    +
    &#xe651;
    +
  • + +
  • + +
    question
    +
    &#xe64f;
    +
  • + +
  • + +
    start-live
    +
    &#xe64e;
    +
  • + +
  • + +
    unfollow
    +
    &#xe6c8;
    +
  • + +
  • + +
    following
    +
    &#xe6d4;
    +
  • + +
  • + +
    break link
    +
    &#xe6c5;
    +
  • + +
  • + +
    icon-beauty-smooth
    +
    &#xe64a;
    +
  • + +
  • + +
    icon-beauty-redness
    +
    &#xe64b;
    +
  • + +
  • + +
    icon-beauty-sharp
    +
    &#xe64c;
    +
  • + +
  • + +
    icon-beauty-whitening
    +
    &#xe64d;
    +
  • + +
  • + +
    volume down
    +
    &#xe647;
    +
  • + +
  • + +
    volume mute
    +
    &#xe648;
    +
  • + +
  • + +
    volume up
    +
    &#xe649;
    +
  • + +
  • + +
    emoji-animated
    +
    &#xe6c4;
    +
  • + +
  • + +
    flip
    +
    &#xe6e6;
    +
  • + +
  • + +
    mic-border
    +
    &#xe6e7;
    +
  • + +
  • + +
    random play
    +
    &#xe6e8;
    +
  • + +
  • + +
    speaker-border
    +
    &#xe6e9;
    +
  • + +
  • + +
    backfullscreen
    +
    &#xe6ca;
    +
  • + +
  • + +
    blocklisk
    +
    &#xe6ce;
    +
  • + +
  • + +
    beauty
    +
    &#xe6d1;
    +
  • + +
  • + +
    fullscreen-exit-fill
    +
    &#xe6d3;
    +
  • + +
  • + +
    fullscreen-fill
    +
    &#xe6d5;
    +
  • + +
  • + +
    fans
    +
    &#xe6d6;
    +
  • + +
  • + +
    fullscreen-mobile
    +
    &#xe6d7;
    +
  • + +
  • + +
    kick
    +
    &#xe6d8;
    +
  • + +
  • + +
    list play
    +
    &#xe6d9;
    +
  • + +
  • + +
    previous
    +
    &#xe6da;
    +
  • + +
  • + +
    data
    +
    &#xe6db;
    +
  • + +
  • + +
    gift effect
    +
    &#xe6dc;
    +
  • + +
  • + +
    gift-border
    +
    &#xe6dd;
    +
  • + +
  • + +
    loop play
    +
    &#xe6de;
    +
  • + +
  • + +
    notice
    +
    &#xe6df;
    +
  • + +
  • + +
    time
    +
    &#xe6e0;
    +
  • + +
  • + +
    volume
    +
    &#xe6e1;
    +
  • + +
  • + +
    playlist
    +
    &#xe6e2;
    +
  • + +
  • + +
    next
    +
    &#xe6e3;
    +
  • + +
  • + +
    music
    +
    &#xe6e4;
    +
  • + +
  • + +
    offMic
    +
    &#xe6e5;
    +
  • + +
  • + +
    pause
    +
    &#xe6c1;
    +
  • + +
  • + +
    icon-fullImage
    +
    &#xe646;
    +
  • + +
  • + +
    icon-camera-fill
    +
    &#xe645;
    +
  • + +
  • + +
    vote
    +
    &#xe6c0;
    +
  • + +
  • + +
    icon-email
    +
    &#xe644;
    +
  • + +
  • + +
    icon_chatroom_bigmenu
    +
    &#xe674;
    +
  • + +
  • + +
    icon_chatroom_smallmenu
    +
    &#xe675;
    +
  • + +
  • + +
    chatroom_mic
    +
    &#xe685;
    +
  • + +
  • + +
    chatroom_on
    +
    &#xe687;
    +
  • + +
  • + +
    exit
    +
    &#xe688;
    +
  • + +
  • + +
    chatroom_more
    +
    &#xe68f;
    +
  • + +
  • + +
    icon_fobidden
    +
    &#xe694;
    +
  • + +
  • + +
    icon_share invtation
    +
    &#xe6ab;
    +
  • + +
  • + +
    ICON_entrance
    +
    &#xe6bc;
    +
  • + +
  • + +
    icon_comunity_delete
    +
    &#xe643;
    +
  • + +
  • + +
    icon_comunity_reply
    +
    &#xe63e;
    +
  • + +
  • + +
    icon_community_like
    +
    &#xe63f;
    +
  • + +
  • + +
    icon_comunity_report
    +
    &#xe640;
    +
  • + +
  • + +
    icon_comunity_topics
    +
    &#xe641;
    +
  • + +
  • + +
    icon_comunity_comment
    +
    &#xe642;
    +
  • + +
  • + +
    icon_menu_Customer
    +
    &#xe6a4;
    +
  • + +
  • + +
    icon_post_collection
    +
    &#xe6c2;
    +
  • + +
  • + +
    icon_post_component
    +
    &#xe6c3;
    +
  • + +
  • + +
    icon_post_recommend
    +
    &#xe6c6;
    +
  • + +
  • + +
    icon_post_highlight
    +
    &#xe6c7;
    +
  • + +
  • + +
    icon_post_viewers
    +
    &#xe6c9;
    +
  • + +
  • + +
    icon_post_chitchat
    +
    &#xe6cc;
    +
  • + +
  • + +
    icon_post_selfies
    +
    &#xe6cd;
    +
  • + +
  • + +
    icon_post_official
    +
    &#xe6cb;
    +
  • + +
  • + +
    icon_order_tip
    +
    &#xe6d0;
    +
  • + +
  • + +
    icon_order_remark
    +
    &#xe6d2;
    +
  • + +
  • + +
    icon_refund
    +
    &#xe63c;
    +
  • + +
  • + +
    icon-post
    +
    &#xe63b;
    +
  • + +
  • + +
    backtop
    +
    &#xe63d;
    +
  • + +
  • + +
    faq
    +
    &#xe6ba;
    +
  • + +
  • + +
    onlineservice
    +
    &#xe6bb;
    +
  • + +
  • + +
    icon-vip
    +
    &#xe638;
    +
  • + +
  • + +
    icon-legends
    +
    &#xe639;
    +
  • + +
  • + +
    icon-influencer
    +
    &#xe63a;
    +
  • + +
  • + +
    icon-comment
    +
    &#xe636;
    +
  • + +
  • + +
    icon-helpCenter
    +
    &#xe634;
    +
  • + +
  • + +
    icon-setting
    +
    &#xe635;
    +
  • + +
  • + +
    icon-reload
    +
    &#xe633;
    +
  • + +
  • + +
    icon-reset
    +
    &#xe631;
    +
  • + +
  • + +
    icon-addCard
    +
    &#xe62e;
    +
  • + +
  • + +
    icon-send
    +
    &#xe630;
    +
  • + +
  • + +
    icon-service-fill
    +
    &#xe632;
    +
  • + +
  • + +
    icon-service
    +
    &#xe62f;
    +
  • + +
  • + +
    icon-random
    +
    &#xe62d;
    +
  • + +
  • + +
    icon-keyboard
    +
    &#xe62c;
    +
  • + +
  • + +
    icon-people
    +
    &#xe629;
    +
  • + +
  • + +
    icon_top
    +
    &#xe62b;
    +
  • + +
  • + +
    icon-order-fill
    +
    &#xe62a;
    +
  • + +
  • + +
    add
    +
    &#xe6b5;
    +
  • + +
  • + +
    reduce
    +
    &#xe6b6;
    +
  • + +
  • + +
    icon-contact
    +
    &#xe616;
    +
  • + +
  • + +
    Warning
    +
    &#xe614;
    +
  • + +
  • + +
    icon_im_purpleface
    +
    &#xe6bd;
    +
  • + +
  • + +
    icon_im_emoji
    +
    &#xe6bf;
    +
  • + +
  • + +
    icon_im_other
    +
    &#xe6cf;
    +
  • + +
  • + +
    chatroom_off
    +
    &#xe686;
    +
  • + +
  • + +
    icon_IM_blocked
    +
    &#xe6b9;
    +
  • + +
  • + +
    icon_call_mute
    +
    &#xe684;
    +
  • + +
  • + +
    icon_call_openmic
    +
    &#xe6b4;
    +
  • + +
  • + +
    icon_call_hangup
    +
    &#xe68d;
    +
  • + +
  • + +
    icon_interact
    +
    &#xe693;
    +
  • + +
  • + +
    quote
    +
    &#xe6b3;
    +
  • + +
  • + +
    checked
    +
    &#xe6b2;
    +
  • + +
  • + +
    report
    +
    &#xe6b1;
    +
  • + +
  • + +
    icon_delete
    +
    &#xe612;
    +
  • + +
  • + +
    icon_public
    +
    &#xe698;
    +
  • + +
  • + +
    icon_private
    +
    &#xe699;
    +
  • + +
  • + +
    icon_uploadimg
    +
    &#xe69c;
    +
  • + +
  • + +
    icon_uploadclip
    +
    &#xe69d;
    +
  • + +
  • + +
    arrow-down-border
    +
    &#xe6af;
    +
  • + +
  • + +
    arrow-left
    +
    &#xe6b0;
    +
  • + +
  • + +
    eye-off
    +
    &#xe6ae;
    +
  • + +
  • + +
    eye-on
    +
    &#xe6ad;
    +
  • + +
  • + +
    arrow-down-fill
    +
    &#xe6ac;
    +
  • + +
  • + +
    arrow-left-border
    +
    &#xe6aa;
    +
  • + +
  • + +
    delete-02
    +
    &#xe628;
    +
  • + +
  • + +
    arrow-down-circle
    +
    &#xe601;
    +
  • + +
  • + +
    arrow-right-border
    +
    &#xe602;
    +
  • + +
  • + +
    arrow-up-fill
    +
    &#xe603;
    +
  • + +
  • + +
    arrow-right
    +
    &#xe604;
    +
  • + +
  • + +
    Chat
    +
    &#xe605;
    +
  • + +
  • + +
    delete
    +
    &#xe607;
    +
  • + +
  • + +
    Create room
    +
    &#xe608;
    +
  • + +
  • + +
    Call
    +
    &#xe609;
    +
  • + +
  • + +
    Like-fill
    +
    &#xe60a;
    +
  • + +
  • + +
    filter-fill
    +
    &#xe60b;
    +
  • + +
  • + +
    live
    +
    &#xe60c;
    +
  • + +
  • + +
    follow
    +
    &#xe60d;
    +
  • + +
  • + +
    Play
    +
    &#xe60e;
    +
  • + +
  • + +
    events
    +
    &#xe60f;
    +
  • + +
  • + +
    More
    +
    &#xe610;
    +
  • + +
  • + +
    matching
    +
    &#xe611;
    +
  • + +
  • + +
    Like
    +
    &#xe613;
    +
  • + +
  • + +
    loading
    +
    &#xe615;
    +
  • + +
  • + +
    Search
    +
    &#xe617;
    +
  • + +
  • + +
    filter
    +
    &#xe618;
    +
  • + +
  • + +
    social-facebook
    +
    &#xe619;
    +
  • + +
  • + +
    select
    +
    &#xe61a;
    +
  • + +
  • + +
    Share-border
    +
    &#xe61b;
    +
  • + +
  • + +
    social-instagram
    +
    &#xe61c;
    +
  • + +
  • + +
    social-twitch
    +
    &#xe61d;
    +
  • + +
  • + +
    Voice
    +
    &#xe61e;
    +
  • + +
  • + +
    social-tiktok
    +
    &#xe61f;
    +
  • + +
  • + +
    social-google
    +
    &#xe620;
    +
  • + +
  • + +
    social-youtube
    +
    &#xe621;
    +
  • + +
  • + +
    social-apple
    +
    &#xe622;
    +
  • + +
  • + +
    icon-kfckfc
    +
    &#xe623;
    +
  • + +
  • + +
    star-fill
    +
    &#xe624;
    +
  • + +
  • + +
    star
    +
    &#xe625;
    +
  • + +
  • + +
    social-discord
    +
    &#xe626;
    +
  • + +
  • + +
    messages
    +
    &#xe627;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1757499081051') format('woff2'),
+       url('iconfont.woff?t=1757499081051') format('woff'),
+       url('iconfont.ttf?t=1757499081051') format('truetype');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + shield +
    +
    .icon-shield +
    +
  • + +
  • + +
    + audits +
    +
    .icon-audits +
    +
  • + +
  • + +
    + post_recommend_fill +
    +
    .icon-post_recommend_fill +
    +
  • + +
  • + +
    + post_Notrecommend_fill +
    +
    .icon-post_Notrecommend_fill +
    +
  • + +
  • + +
    + voice_msg +
    +
    .icon-voice_msg +
    +
  • + +
  • + +
    + clear +
    +
    .icon-clear +
    +
  • + +
  • + +
    + prompt +
    +
    .icon-prompt +
    +
  • + +
  • + +
    + statistics +
    +
    .icon-statistics +
    +
  • + +
  • + +
    + sort_az +
    +
    .icon-sort_az +
    +
  • + +
  • + +
    + orderlobby +
    +
    .icon-orderlobby +
    +
  • + +
  • + +
    + link +
    +
    .icon-link +
    +
  • + +
  • + +
    + minimize +
    +
    .icon-minimize +
    +
  • + +
  • + +
    + icon_watchtogether +
    +
    .icon-icon_watchtogether +
    +
  • + +
  • + +
    + seat +
    +
    .icon-seat +
    +
  • + +
  • + +
    + mute all +
    +
    .icon-mute1 +
    +
  • + +
  • + +
    + entrance sound +
    +
    .icon-a-entrancesound +
    +
  • + +
  • + +
    + sort +
    +
    .icon-sort +
    +
  • + +
  • + +
    + VIP_nocolor +
    +
    .icon-VIP_nocolor +
    +
  • + +
  • + +
    + purchaseorder +
    +
    .icon-purchaseorder +
    +
  • + +
  • + +
    + placeorder +
    +
    .icon-placeorder +
    +
  • + +
  • + +
    + connect +
    +
    .icon-connect +
    +
  • + +
  • + +
    + tag +
    +
    .icon-tag +
    +
  • + +
  • + +
    + card +
    +
    .icon-card +
    +
  • + +
  • + +
    + Steam Deck +
    +
    .icon-Steam +
    +
  • + +
  • + +
    + Android +
    +
    .icon-Android +
    +
  • + +
  • + +
    + Xbox +
    +
    .icon-Xbox +
    +
  • + +
  • + +
    + PS +
    +
    .icon-PS +
    +
  • + +
  • + +
    + Switch +
    +
    .icon-Switch +
    +
  • + +
  • + +
    + PC +
    +
    .icon-PC +
    +
  • + +
  • + +
    + thread +
    +
    .icon-thread +
    +
  • + +
  • + +
    + icon_post_notrecommend +
    +
    .icon-icon_post_Notrecommend +
    +
  • + +
  • + +
    + coupon-border +
    +
    .icon-coupon-border +
    +
  • + +
  • + +
    + date +
    +
    .icon-date +
    +
  • + +
  • + +
    + mic request border +
    +
    .icon-a-micrequestborder +
    +
  • + +
  • + +
    + mic request +
    +
    .icon-a-micrequest +
    +
  • + +
  • + +
    + icon-public-border +
    +
    .icon-public-border +
    +
  • + +
  • + +
    + icon-private-border +
    +
    .icon-private-border +
    +
  • + +
  • + +
    + female +
    +
    .icon-female +
    +
  • + +
  • + +
    + nonconforming +
    +
    .icon-nonconforming +
    +
  • + +
  • + +
    + male +
    +
    .icon-male +
    +
  • + +
  • + +
    + icon_exclamation +
    +
    .icon-icon_hint +
    +
  • + +
  • + +
    + stop +
    +
    .icon-stop +
    +
  • + +
  • + +
    + language +
    +
    .icon-language +
    +
  • + +
  • + +
    + account +
    +
    .icon-account +
    +
  • + +
  • + +
    + IM-setting +
    +
    .icon-IM-setting +
    +
  • + +
  • + +
    + info +
    +
    .icon-info +
    +
  • + +
  • + +
    + copy +
    +
    .icon-copy +
    +
  • + +
  • + +
    + icon-drag +
    +
    .icon-icon-drag +
    +
  • + +
  • + +
    + Invite to mic +
    +
    .icon-a-Invitetomic +
    +
  • + +
  • + +
    + voice-live +
    +
    .icon-voice-live +
    +
  • + +
  • + +
    + question +
    +
    .icon-question +
    +
  • + +
  • + +
    + start-live +
    +
    .icon-a-createlive +
    +
  • + +
  • + +
    + unfollow +
    +
    .icon-unfollow +
    +
  • + +
  • + +
    + following +
    +
    .icon-following +
    +
  • + +
  • + +
    + break link +
    +
    .icon-a-breaklink +
    +
  • + +
  • + +
    + icon-beauty-smooth +
    +
    .icon-icon-beauty-smooth +
    +
  • + +
  • + +
    + icon-beauty-redness +
    +
    .icon-icon-beauty-redness +
    +
  • + +
  • + +
    + icon-beauty-sharp +
    +
    .icon-icon-beauty-sharp +
    +
  • + +
  • + +
    + icon-beauty-whitening +
    +
    .icon-icon-beauty-whitening +
    +
  • + +
  • + +
    + volume down +
    +
    .icon-a-volumedown +
    +
  • + +
  • + +
    + volume mute +
    +
    .icon-a-volumemute +
    +
  • + +
  • + +
    + volume up +
    +
    .icon-a-volumeup +
    +
  • + +
  • + +
    + emoji-animated +
    +
    .icon-emoji-animated +
    +
  • + +
  • + +
    + flip +
    +
    .icon-flip +
    +
  • + +
  • + +
    + mic-border +
    +
    .icon-mic-border +
    +
  • + +
  • + +
    + random play +
    +
    .icon-a-randomplay +
    +
  • + +
  • + +
    + speaker-border +
    +
    .icon-speaker-border +
    +
  • + +
  • + +
    + backfullscreen +
    +
    .icon-backfullscreen +
    +
  • + +
  • + +
    + blocklisk +
    +
    .icon-blocklisk +
    +
  • + +
  • + +
    + beauty +
    +
    .icon-beauty +
    +
  • + +
  • + +
    + fullscreen-exit-fill +
    +
    .icon-fullscreen-exit-fill +
    +
  • + +
  • + +
    + fullscreen-fill +
    +
    .icon-fullscreen-fill +
    +
  • + +
  • + +
    + fans +
    +
    .icon-fans +
    +
  • + +
  • + +
    + fullscreen-mobile +
    +
    .icon-fullscreen-mobile +
    +
  • + +
  • + +
    + kick +
    +
    .icon-kick +
    +
  • + +
  • + +
    + list play +
    +
    .icon-a-listplay +
    +
  • + +
  • + +
    + previous +
    +
    .icon-previous +
    +
  • + +
  • + +
    + data +
    +
    .icon-data +
    +
  • + +
  • + +
    + gift effect +
    +
    .icon-a-gifteffect +
    +
  • + +
  • + +
    + gift-border +
    +
    .icon-gift-border +
    +
  • + +
  • + +
    + loop play +
    +
    .icon-a-loopplay +
    +
  • + +
  • + +
    + notice +
    +
    .icon-notice +
    +
  • + +
  • + +
    + time +
    +
    .icon-time +
    +
  • + +
  • + +
    + volume +
    +
    .icon-volume +
    +
  • + +
  • + +
    + playlist +
    +
    .icon-playlist +
    +
  • + +
  • + +
    + next +
    +
    .icon-next +
    +
  • + +
  • + +
    + music +
    +
    .icon-music +
    +
  • + +
  • + +
    + offMic +
    +
    .icon-offMic +
    +
  • + +
  • + +
    + pause +
    +
    .icon-pause +
    +
  • + +
  • + +
    + icon-fullImage +
    +
    .icon-icon-fullImage +
    +
  • + +
  • + +
    + icon-camera-fill +
    +
    .icon-icon-camera-fill +
    +
  • + +
  • + +
    + vote +
    +
    .icon-vote +
    +
  • + +
  • + +
    + icon-email +
    +
    .icon-icon-email +
    +
  • + +
  • + +
    + icon_chatroom_bigmenu +
    +
    .icon-icon_chatroom_bigmenu +
    +
  • + +
  • + +
    + icon_chatroom_smallmenu +
    +
    .icon-icon_chatroom_smallmenu +
    +
  • + +
  • + +
    + chatroom_mic +
    +
    .icon-icon_chatroom_mic +
    +
  • + +
  • + +
    + chatroom_on +
    +
    .icon-icon_on +
    +
  • + +
  • + +
    + exit +
    +
    .icon-icon_exit +
    +
  • + +
  • + +
    + chatroom_more +
    +
    .icon-icon_chatroom_more +
    +
  • + +
  • + +
    + icon_fobidden +
    +
    .icon-jinmai +
    +
  • + +
  • + +
    + icon_share invtation +
    +
    .icon-a-icon_shareinvtation +
    +
  • + +
  • + +
    + ICON_entrance +
    +
    .icon-icon_entrance +
    +
  • + +
  • + +
    + icon_comunity_delete +
    +
    .icon-icon_comunity_delete +
    +
  • + +
  • + +
    + icon_comunity_reply +
    +
    .icon-icon_comunity_reply +
    +
  • + +
  • + +
    + icon_community_like +
    +
    .icon-icon_community_like +
    +
  • + +
  • + +
    + icon_comunity_report +
    +
    .icon-icon_comunity_report +
    +
  • + +
  • + +
    + icon_comunity_topics +
    +
    .icon-icon_comunity_topics +
    +
  • + +
  • + +
    + icon_comunity_comment +
    +
    .icon-icon_comunity_comment +
    +
  • + +
  • + +
    + icon_menu_Customer +
    +
    .icon-icon_menu_Customer +
    +
  • + +
  • + +
    + icon_post_collection +
    +
    .icon-icon_post_collection +
    +
  • + +
  • + +
    + icon_post_component +
    +
    .icon-icon_post_component +
    +
  • + +
  • + +
    + icon_post_recommend +
    +
    .icon-icon_post_recommend +
    +
  • + +
  • + +
    + icon_post_highlight +
    +
    .icon-icon_post_highlight +
    +
  • + +
  • + +
    + icon_post_viewers +
    +
    .icon-icon_post_viewers +
    +
  • + +
  • + +
    + icon_post_chitchat +
    +
    .icon-icon_post_chitchat +
    +
  • + +
  • + +
    + icon_post_selfies +
    +
    .icon-icon_post_selfies +
    +
  • + +
  • + +
    + icon_post_official +
    +
    .icon-icon_post_official +
    +
  • + +
  • + +
    + icon_order_tip +
    +
    .icon-icon_order_tip +
    +
  • + +
  • + +
    + icon_order_remark +
    +
    .icon-icon_order_remark +
    +
  • + +
  • + +
    + icon_refund +
    +
    .icon-icon_refund +
    +
  • + +
  • + +
    + icon-post +
    +
    .icon-icon-post +
    +
  • + +
  • + +
    + backtop +
    +
    .icon-backtop +
    +
  • + +
  • + +
    + faq +
    +
    .icon-faq +
    +
  • + +
  • + +
    + onlineservice +
    +
    .icon-onlineservice +
    +
  • + +
  • + +
    + icon-vip +
    +
    .icon-icon-vip +
    +
  • + +
  • + +
    + icon-legends +
    +
    .icon-icon-legends +
    +
  • + +
  • + +
    + icon-influencer +
    +
    .icon-icon-influencer +
    +
  • + +
  • + +
    + icon-comment +
    +
    .icon-icon-comment +
    +
  • + +
  • + +
    + icon-helpCenter +
    +
    .icon-icon-helpCenter +
    +
  • + +
  • + +
    + icon-setting +
    +
    .icon-icon-setting +
    +
  • + +
  • + +
    + icon-reload +
    +
    .icon-icon-reload +
    +
  • + +
  • + +
    + icon-reset +
    +
    .icon-icon-reset +
    +
  • + +
  • + +
    + icon-addCard +
    +
    .icon-icon-addCard +
    +
  • + +
  • + +
    + icon-send +
    +
    .icon-icon-send +
    +
  • + +
  • + +
    + icon-service-fill +
    +
    .icon-icon-service-fill +
    +
  • + +
  • + +
    + icon-service +
    +
    .icon-icon-service +
    +
  • + +
  • + +
    + icon-random +
    +
    .icon-icon-random +
    +
  • + +
  • + +
    + icon-keyboard +
    +
    .icon-icon-keyboard +
    +
  • + +
  • + +
    + icon-people +
    +
    .icon-icon-people +
    +
  • + +
  • + +
    + icon_top +
    +
    .icon-icon_top +
    +
  • + +
  • + +
    + icon-order-fill +
    +
    .icon-icon-order-fill +
    +
  • + +
  • + +
    + add +
    +
    .icon-add +
    +
  • + +
  • + +
    + reduce +
    +
    .icon-reduce +
    +
  • + +
  • + +
    + icon-contact +
    +
    .icon-icon-contact +
    +
  • + +
  • + +
    + Warning +
    +
    .icon-Warning +
    +
  • + +
  • + +
    + icon_im_purpleface +
    +
    .icon-icon_im_purpleface +
    +
  • + +
  • + +
    + icon_im_emoji +
    +
    .icon-icon_im_emoji +
    +
  • + +
  • + +
    + icon_im_other +
    +
    .icon-icon_im_other +
    +
  • + +
  • + +
    + chatroom_off +
    +
    .icon-msgmute +
    +
  • + +
  • + +
    + icon_IM_blocked +
    +
    .icon-block +
    +
  • + +
  • + +
    + icon_call_mute +
    +
    .icon-mute +
    +
  • + +
  • + +
    + icon_call_openmic +
    +
    .icon-open-mic +
    +
  • + +
  • + +
    + icon_call_hangup +
    +
    .icon-hang-up +
    +
  • + +
  • + +
    + icon_interact +
    +
    .icon-emoji +
    +
  • + +
  • + +
    + quote +
    +
    .icon-quote +
    +
  • + +
  • + +
    + checked +
    +
    .icon-checked +
    +
  • + +
  • + +
    + report +
    +
    .icon-report +
    +
  • + +
  • + +
    + icon_delete +
    +
    .icon-trashcan +
    +
  • + +
  • + +
    + icon_public +
    +
    .icon-public +
    +
  • + +
  • + +
    + icon_private +
    +
    .icon-private +
    +
  • + +
  • + +
    + icon_uploadimg +
    +
    .icon-uploadimg +
    +
  • + +
  • + +
    + icon_uploadclip +
    +
    .icon-uploadclip +
    +
  • + +
  • + +
    + arrow-down-border +
    +
    .icon-arrow-down-border +
    +
  • + +
  • + +
    + arrow-left +
    +
    .icon-arrow-left +
    +
  • + +
  • + +
    + eye-off +
    +
    .icon-eye-off +
    +
  • + +
  • + +
    + eye-on +
    +
    .icon-eye-on +
    +
  • + +
  • + +
    + arrow-down-fill +
    +
    .icon-arrow-down-fill +
    +
  • + +
  • + +
    + arrow-left-border +
    +
    .icon-arrow-left-border +
    +
  • + +
  • + +
    + delete-02 +
    +
    .icon-delete-02 +
    +
  • + +
  • + +
    + arrow-down-circle +
    +
    .icon-arrow-down-circle +
    +
  • + +
  • + +
    + arrow-right-border +
    +
    .icon-arrow-right-border +
    +
  • + +
  • + +
    + arrow-up-fill +
    +
    .icon-arrow-up-fill +
    +
  • + +
  • + +
    + arrow-right +
    +
    .icon-arrow-right +
    +
  • + +
  • + +
    + Chat +
    +
    .icon-Chat +
    +
  • + +
  • + +
    + delete +
    +
    .icon-close +
    +
  • + +
  • + +
    + Create room +
    +
    .icon-a-Createroom +
    +
  • + +
  • + +
    + Call +
    +
    .icon-Call +
    +
  • + +
  • + +
    + Like-fill +
    +
    .icon-Like-fill +
    +
  • + +
  • + +
    + filter-fill +
    +
    .icon-filter-fill +
    +
  • + +
  • + +
    + live +
    +
    .icon-live +
    +
  • + +
  • + +
    + follow +
    +
    .icon-follow +
    +
  • + +
  • + +
    + Play +
    +
    .icon-Play +
    +
  • + +
  • + +
    + events +
    +
    .icon-events +
    +
  • + +
  • + +
    + More +
    +
    .icon-More +
    +
  • + +
  • + +
    + matching +
    +
    .icon-matching +
    +
  • + +
  • + +
    + Like +
    +
    .icon-Like +
    +
  • + +
  • + +
    + loading +
    +
    .icon-loading +
    +
  • + +
  • + +
    + Search +
    +
    .icon-Search +
    +
  • + +
  • + +
    + filter +
    +
    .icon-filter +
    +
  • + +
  • + +
    + social-facebook +
    +
    .icon-social-facebook +
    +
  • + +
  • + +
    + select +
    +
    .icon-select +
    +
  • + +
  • + +
    + Share-border +
    +
    .icon-Share-border +
    +
  • + +
  • + +
    + social-instagram +
    +
    .icon-social-instagram +
    +
  • + +
  • + +
    + social-twitch +
    +
    .icon-social-twitch +
    +
  • + +
  • + +
    + Voice +
    +
    .icon-Voice +
    +
  • + +
  • + +
    + social-tiktok +
    +
    .icon-social-tiktok +
    +
  • + +
  • + +
    + social-google +
    +
    .icon-social-google +
    +
  • + +
  • + +
    + social-youtube +
    +
    .icon-social-youtube +
    +
  • + +
  • + +
    + social-apple +
    +
    .icon-social-apple +
    +
  • + +
  • + +
    + icon-kfckfc +
    +
    .icon-social-twitter +
    +
  • + +
  • + +
    + star-fill +
    +
    .icon-star-fill +
    +
  • + +
  • + +
    + star +
    +
    .icon-star +
    +
  • + +
  • + +
    + social-discord +
    +
    .icon-social-discord +
    +
  • + +
  • + +
    + messages +
    +
    .icon-messages +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    shield
    +
    #icon-shield
    +
  • + +
  • + +
    audits
    +
    #icon-audits
    +
  • + +
  • + +
    post_recommend_fill
    +
    #icon-post_recommend_fill
    +
  • + +
  • + +
    post_Notrecommend_fill
    +
    #icon-post_Notrecommend_fill
    +
  • + +
  • + +
    voice_msg
    +
    #icon-voice_msg
    +
  • + +
  • + +
    clear
    +
    #icon-clear
    +
  • + +
  • + +
    prompt
    +
    #icon-prompt
    +
  • + +
  • + +
    statistics
    +
    #icon-statistics
    +
  • + +
  • + +
    sort_az
    +
    #icon-sort_az
    +
  • + +
  • + +
    orderlobby
    +
    #icon-orderlobby
    +
  • + +
  • + +
    link
    +
    #icon-link
    +
  • + +
  • + +
    minimize
    +
    #icon-minimize
    +
  • + +
  • + +
    icon_watchtogether
    +
    #icon-icon_watchtogether
    +
  • + +
  • + +
    seat
    +
    #icon-seat
    +
  • + +
  • + +
    mute all
    +
    #icon-mute1
    +
  • + +
  • + +
    entrance sound
    +
    #icon-a-entrancesound
    +
  • + +
  • + +
    sort
    +
    #icon-sort
    +
  • + +
  • + +
    VIP_nocolor
    +
    #icon-VIP_nocolor
    +
  • + +
  • + +
    purchaseorder
    +
    #icon-purchaseorder
    +
  • + +
  • + +
    placeorder
    +
    #icon-placeorder
    +
  • + +
  • + +
    connect
    +
    #icon-connect
    +
  • + +
  • + +
    tag
    +
    #icon-tag
    +
  • + +
  • + +
    card
    +
    #icon-card
    +
  • + +
  • + +
    Steam Deck
    +
    #icon-Steam
    +
  • + +
  • + +
    Android
    +
    #icon-Android
    +
  • + +
  • + +
    Xbox
    +
    #icon-Xbox
    +
  • + +
  • + +
    PS
    +
    #icon-PS
    +
  • + +
  • + +
    Switch
    +
    #icon-Switch
    +
  • + +
  • + +
    PC
    +
    #icon-PC
    +
  • + +
  • + +
    thread
    +
    #icon-thread
    +
  • + +
  • + +
    icon_post_notrecommend
    +
    #icon-icon_post_Notrecommend
    +
  • + +
  • + +
    coupon-border
    +
    #icon-coupon-border
    +
  • + +
  • + +
    date
    +
    #icon-date
    +
  • + +
  • + +
    mic request border
    +
    #icon-a-micrequestborder
    +
  • + +
  • + +
    mic request
    +
    #icon-a-micrequest
    +
  • + +
  • + +
    icon-public-border
    +
    #icon-public-border
    +
  • + +
  • + +
    icon-private-border
    +
    #icon-private-border
    +
  • + +
  • + +
    female
    +
    #icon-female
    +
  • + +
  • + +
    nonconforming
    +
    #icon-nonconforming
    +
  • + +
  • + +
    male
    +
    #icon-male
    +
  • + +
  • + +
    icon_exclamation
    +
    #icon-icon_hint
    +
  • + +
  • + +
    stop
    +
    #icon-stop
    +
  • + +
  • + +
    language
    +
    #icon-language
    +
  • + +
  • + +
    account
    +
    #icon-account
    +
  • + +
  • + +
    IM-setting
    +
    #icon-IM-setting
    +
  • + +
  • + +
    info
    +
    #icon-info
    +
  • + +
  • + +
    copy
    +
    #icon-copy
    +
  • + +
  • + +
    icon-drag
    +
    #icon-icon-drag
    +
  • + +
  • + +
    Invite to mic
    +
    #icon-a-Invitetomic
    +
  • + +
  • + +
    voice-live
    +
    #icon-voice-live
    +
  • + +
  • + +
    question
    +
    #icon-question
    +
  • + +
  • + +
    start-live
    +
    #icon-a-createlive
    +
  • + +
  • + +
    unfollow
    +
    #icon-unfollow
    +
  • + +
  • + +
    following
    +
    #icon-following
    +
  • + +
  • + +
    break link
    +
    #icon-a-breaklink
    +
  • + +
  • + +
    icon-beauty-smooth
    +
    #icon-icon-beauty-smooth
    +
  • + +
  • + +
    icon-beauty-redness
    +
    #icon-icon-beauty-redness
    +
  • + +
  • + +
    icon-beauty-sharp
    +
    #icon-icon-beauty-sharp
    +
  • + +
  • + +
    icon-beauty-whitening
    +
    #icon-icon-beauty-whitening
    +
  • + +
  • + +
    volume down
    +
    #icon-a-volumedown
    +
  • + +
  • + +
    volume mute
    +
    #icon-a-volumemute
    +
  • + +
  • + +
    volume up
    +
    #icon-a-volumeup
    +
  • + +
  • + +
    emoji-animated
    +
    #icon-emoji-animated
    +
  • + +
  • + +
    flip
    +
    #icon-flip
    +
  • + +
  • + +
    mic-border
    +
    #icon-mic-border
    +
  • + +
  • + +
    random play
    +
    #icon-a-randomplay
    +
  • + +
  • + +
    speaker-border
    +
    #icon-speaker-border
    +
  • + +
  • + +
    backfullscreen
    +
    #icon-backfullscreen
    +
  • + +
  • + +
    blocklisk
    +
    #icon-blocklisk
    +
  • + +
  • + +
    beauty
    +
    #icon-beauty
    +
  • + +
  • + +
    fullscreen-exit-fill
    +
    #icon-fullscreen-exit-fill
    +
  • + +
  • + +
    fullscreen-fill
    +
    #icon-fullscreen-fill
    +
  • + +
  • + +
    fans
    +
    #icon-fans
    +
  • + +
  • + +
    fullscreen-mobile
    +
    #icon-fullscreen-mobile
    +
  • + +
  • + +
    kick
    +
    #icon-kick
    +
  • + +
  • + +
    list play
    +
    #icon-a-listplay
    +
  • + +
  • + +
    previous
    +
    #icon-previous
    +
  • + +
  • + +
    data
    +
    #icon-data
    +
  • + +
  • + +
    gift effect
    +
    #icon-a-gifteffect
    +
  • + +
  • + +
    gift-border
    +
    #icon-gift-border
    +
  • + +
  • + +
    loop play
    +
    #icon-a-loopplay
    +
  • + +
  • + +
    notice
    +
    #icon-notice
    +
  • + +
  • + +
    time
    +
    #icon-time
    +
  • + +
  • + +
    volume
    +
    #icon-volume
    +
  • + +
  • + +
    playlist
    +
    #icon-playlist
    +
  • + +
  • + +
    next
    +
    #icon-next
    +
  • + +
  • + +
    music
    +
    #icon-music
    +
  • + +
  • + +
    offMic
    +
    #icon-offMic
    +
  • + +
  • + +
    pause
    +
    #icon-pause
    +
  • + +
  • + +
    icon-fullImage
    +
    #icon-icon-fullImage
    +
  • + +
  • + +
    icon-camera-fill
    +
    #icon-icon-camera-fill
    +
  • + +
  • + +
    vote
    +
    #icon-vote
    +
  • + +
  • + +
    icon-email
    +
    #icon-icon-email
    +
  • + +
  • + +
    icon_chatroom_bigmenu
    +
    #icon-icon_chatroom_bigmenu
    +
  • + +
  • + +
    icon_chatroom_smallmenu
    +
    #icon-icon_chatroom_smallmenu
    +
  • + +
  • + +
    chatroom_mic
    +
    #icon-icon_chatroom_mic
    +
  • + +
  • + +
    chatroom_on
    +
    #icon-icon_on
    +
  • + +
  • + +
    exit
    +
    #icon-icon_exit
    +
  • + +
  • + +
    chatroom_more
    +
    #icon-icon_chatroom_more
    +
  • + +
  • + +
    icon_fobidden
    +
    #icon-jinmai
    +
  • + +
  • + +
    icon_share invtation
    +
    #icon-a-icon_shareinvtation
    +
  • + +
  • + +
    ICON_entrance
    +
    #icon-icon_entrance
    +
  • + +
  • + +
    icon_comunity_delete
    +
    #icon-icon_comunity_delete
    +
  • + +
  • + +
    icon_comunity_reply
    +
    #icon-icon_comunity_reply
    +
  • + +
  • + +
    icon_community_like
    +
    #icon-icon_community_like
    +
  • + +
  • + +
    icon_comunity_report
    +
    #icon-icon_comunity_report
    +
  • + +
  • + +
    icon_comunity_topics
    +
    #icon-icon_comunity_topics
    +
  • + +
  • + +
    icon_comunity_comment
    +
    #icon-icon_comunity_comment
    +
  • + +
  • + +
    icon_menu_Customer
    +
    #icon-icon_menu_Customer
    +
  • + +
  • + +
    icon_post_collection
    +
    #icon-icon_post_collection
    +
  • + +
  • + +
    icon_post_component
    +
    #icon-icon_post_component
    +
  • + +
  • + +
    icon_post_recommend
    +
    #icon-icon_post_recommend
    +
  • + +
  • + +
    icon_post_highlight
    +
    #icon-icon_post_highlight
    +
  • + +
  • + +
    icon_post_viewers
    +
    #icon-icon_post_viewers
    +
  • + +
  • + +
    icon_post_chitchat
    +
    #icon-icon_post_chitchat
    +
  • + +
  • + +
    icon_post_selfies
    +
    #icon-icon_post_selfies
    +
  • + +
  • + +
    icon_post_official
    +
    #icon-icon_post_official
    +
  • + +
  • + +
    icon_order_tip
    +
    #icon-icon_order_tip
    +
  • + +
  • + +
    icon_order_remark
    +
    #icon-icon_order_remark
    +
  • + +
  • + +
    icon_refund
    +
    #icon-icon_refund
    +
  • + +
  • + +
    icon-post
    +
    #icon-icon-post
    +
  • + +
  • + +
    backtop
    +
    #icon-backtop
    +
  • + +
  • + +
    faq
    +
    #icon-faq
    +
  • + +
  • + +
    onlineservice
    +
    #icon-onlineservice
    +
  • + +
  • + +
    icon-vip
    +
    #icon-icon-vip
    +
  • + +
  • + +
    icon-legends
    +
    #icon-icon-legends
    +
  • + +
  • + +
    icon-influencer
    +
    #icon-icon-influencer
    +
  • + +
  • + +
    icon-comment
    +
    #icon-icon-comment
    +
  • + +
  • + +
    icon-helpCenter
    +
    #icon-icon-helpCenter
    +
  • + +
  • + +
    icon-setting
    +
    #icon-icon-setting
    +
  • + +
  • + +
    icon-reload
    +
    #icon-icon-reload
    +
  • + +
  • + +
    icon-reset
    +
    #icon-icon-reset
    +
  • + +
  • + +
    icon-addCard
    +
    #icon-icon-addCard
    +
  • + +
  • + +
    icon-send
    +
    #icon-icon-send
    +
  • + +
  • + +
    icon-service-fill
    +
    #icon-icon-service-fill
    +
  • + +
  • + +
    icon-service
    +
    #icon-icon-service
    +
  • + +
  • + +
    icon-random
    +
    #icon-icon-random
    +
  • + +
  • + +
    icon-keyboard
    +
    #icon-icon-keyboard
    +
  • + +
  • + +
    icon-people
    +
    #icon-icon-people
    +
  • + +
  • + +
    icon_top
    +
    #icon-icon_top
    +
  • + +
  • + +
    icon-order-fill
    +
    #icon-icon-order-fill
    +
  • + +
  • + +
    add
    +
    #icon-add
    +
  • + +
  • + +
    reduce
    +
    #icon-reduce
    +
  • + +
  • + +
    icon-contact
    +
    #icon-icon-contact
    +
  • + +
  • + +
    Warning
    +
    #icon-Warning
    +
  • + +
  • + +
    icon_im_purpleface
    +
    #icon-icon_im_purpleface
    +
  • + +
  • + +
    icon_im_emoji
    +
    #icon-icon_im_emoji
    +
  • + +
  • + +
    icon_im_other
    +
    #icon-icon_im_other
    +
  • + +
  • + +
    chatroom_off
    +
    #icon-msgmute
    +
  • + +
  • + +
    icon_IM_blocked
    +
    #icon-block
    +
  • + +
  • + +
    icon_call_mute
    +
    #icon-mute
    +
  • + +
  • + +
    icon_call_openmic
    +
    #icon-open-mic
    +
  • + +
  • + +
    icon_call_hangup
    +
    #icon-hang-up
    +
  • + +
  • + +
    icon_interact
    +
    #icon-emoji
    +
  • + +
  • + +
    quote
    +
    #icon-quote
    +
  • + +
  • + +
    checked
    +
    #icon-checked
    +
  • + +
  • + +
    report
    +
    #icon-report
    +
  • + +
  • + +
    icon_delete
    +
    #icon-trashcan
    +
  • + +
  • + +
    icon_public
    +
    #icon-public
    +
  • + +
  • + +
    icon_private
    +
    #icon-private
    +
  • + +
  • + +
    icon_uploadimg
    +
    #icon-uploadimg
    +
  • + +
  • + +
    icon_uploadclip
    +
    #icon-uploadclip
    +
  • + +
  • + +
    arrow-down-border
    +
    #icon-arrow-down-border
    +
  • + +
  • + +
    arrow-left
    +
    #icon-arrow-left
    +
  • + +
  • + +
    eye-off
    +
    #icon-eye-off
    +
  • + +
  • + +
    eye-on
    +
    #icon-eye-on
    +
  • + +
  • + +
    arrow-down-fill
    +
    #icon-arrow-down-fill
    +
  • + +
  • + +
    arrow-left-border
    +
    #icon-arrow-left-border
    +
  • + +
  • + +
    delete-02
    +
    #icon-delete-02
    +
  • + +
  • + +
    arrow-down-circle
    +
    #icon-arrow-down-circle
    +
  • + +
  • + +
    arrow-right-border
    +
    #icon-arrow-right-border
    +
  • + +
  • + +
    arrow-up-fill
    +
    #icon-arrow-up-fill
    +
  • + +
  • + +
    arrow-right
    +
    #icon-arrow-right
    +
  • + +
  • + +
    Chat
    +
    #icon-Chat
    +
  • + +
  • + +
    delete
    +
    #icon-close
    +
  • + +
  • + +
    Create room
    +
    #icon-a-Createroom
    +
  • + +
  • + +
    Call
    +
    #icon-Call
    +
  • + +
  • + +
    Like-fill
    +
    #icon-Like-fill
    +
  • + +
  • + +
    filter-fill
    +
    #icon-filter-fill
    +
  • + +
  • + +
    live
    +
    #icon-live
    +
  • + +
  • + +
    follow
    +
    #icon-follow
    +
  • + +
  • + +
    Play
    +
    #icon-Play
    +
  • + +
  • + +
    events
    +
    #icon-events
    +
  • + +
  • + +
    More
    +
    #icon-More
    +
  • + +
  • + +
    matching
    +
    #icon-matching
    +
  • + +
  • + +
    Like
    +
    #icon-Like
    +
  • + +
  • + +
    loading
    +
    #icon-loading
    +
  • + +
  • + +
    Search
    +
    #icon-Search
    +
  • + +
  • + +
    filter
    +
    #icon-filter
    +
  • + +
  • + +
    social-facebook
    +
    #icon-social-facebook
    +
  • + +
  • + +
    select
    +
    #icon-select
    +
  • + +
  • + +
    Share-border
    +
    #icon-Share-border
    +
  • + +
  • + +
    social-instagram
    +
    #icon-social-instagram
    +
  • + +
  • + +
    social-twitch
    +
    #icon-social-twitch
    +
  • + +
  • + +
    Voice
    +
    #icon-Voice
    +
  • + +
  • + +
    social-tiktok
    +
    #icon-social-tiktok
    +
  • + +
  • + +
    social-google
    +
    #icon-social-google
    +
  • + +
  • + +
    social-youtube
    +
    #icon-social-youtube
    +
  • + +
  • + +
    social-apple
    +
    #icon-social-apple
    +
  • + +
  • + +
    icon-kfckfc
    +
    #icon-social-twitter
    +
  • + +
  • + +
    star-fill
    +
    #icon-star-fill
    +
  • + +
  • + +
    star
    +
    #icon-star
    +
  • + +
  • + +
    social-discord
    +
    #icon-social-discord
    +
  • + +
  • + +
    messages
    +
    #icon-messages
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/public/font/iconfont.css b/public/font/iconfont.css new file mode 100644 index 0000000..9dc0fff --- /dev/null +++ b/public/font/iconfont.css @@ -0,0 +1,831 @@ +@font-face { + font-family: "iconfont"; /* Project id 4975639 */ + src: url('iconfont.woff2?t=1757499081051') format('woff2'), + url('iconfont.woff?t=1757499081051') format('woff'), + url('iconfont.ttf?t=1757499081051') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-shield:before { + content: "\e6fa"; +} + +.icon-audits:before { + content: "\e6fb"; +} + +.icon-post_recommend_fill:before { + content: "\e6f9"; +} + +.icon-post_Notrecommend_fill:before { + content: "\e6f8"; +} + +.icon-voice_msg:before { + content: "\e652"; +} + +.icon-clear:before { + content: "\e657"; +} + +.icon-prompt:before { + content: "\e66f"; +} + +.icon-statistics:before { + content: "\e66e"; +} + +.icon-sort_az:before { + content: "\e66d"; +} + +.icon-orderlobby:before { + content: "\e6f7"; +} + +.icon-link:before { + content: "\e6f6"; +} + +.icon-minimize:before { + content: "\e66c"; +} + +.icon-icon_watchtogether:before { + content: "\e6f5"; +} + +.icon-seat:before { + content: "\e6f4"; +} + +.icon-mute1:before { + content: "\e66b"; +} + +.icon-a-entrancesound:before { + content: "\e6f3"; +} + +.icon-sort:before { + content: "\e66a"; +} + +.icon-VIP_nocolor:before { + content: "\e6f2"; +} + +.icon-purchaseorder:before { + content: "\e6f0"; +} + +.icon-placeorder:before { + content: "\e6f1"; +} + +.icon-connect:before { + content: "\e669"; +} + +.icon-tag:before { + content: "\e668"; +} + +.icon-card:before { + content: "\e667"; +} + +.icon-Steam:before { + content: "\e666"; +} + +.icon-Android:before { + content: "\e661"; +} + +.icon-Xbox:before { + content: "\e662"; +} + +.icon-PS:before { + content: "\e663"; +} + +.icon-Switch:before { + content: "\e664"; +} + +.icon-PC:before { + content: "\e665"; +} + +.icon-thread:before { + content: "\e6ef"; +} + +.icon-icon_post_Notrecommend:before { + content: "\e6ee"; +} + +.icon-coupon-border:before { + content: "\e660"; +} + +.icon-date:before { + content: "\e6ed"; +} + +.icon-a-micrequestborder:before { + content: "\e65e"; +} + +.icon-a-micrequest:before { + content: "\e65f"; +} + +.icon-public-border:before { + content: "\e65c"; +} + +.icon-private-border:before { + content: "\e65d"; +} + +.icon-female:before { + content: "\e659"; +} + +.icon-nonconforming:before { + content: "\e65a"; +} + +.icon-male:before { + content: "\e65b"; +} + +.icon-icon_hint:before { + content: "\e6ec"; +} + +.icon-stop:before { + content: "\e658"; +} + +.icon-language:before { + content: "\e654"; +} + +.icon-account:before { + content: "\e655"; +} + +.icon-IM-setting:before { + content: "\e656"; +} + +.icon-info:before { + content: "\e653"; +} + +.icon-copy:before { + content: "\e6eb"; +} + +.icon-icon-drag:before { + content: "\e650"; +} + +.icon-a-Invitetomic:before { + content: "\e6ea"; +} + +.icon-voice-live:before { + content: "\e651"; +} + +.icon-question:before { + content: "\e64f"; +} + +.icon-a-createlive:before { + content: "\e64e"; +} + +.icon-unfollow:before { + content: "\e6c8"; +} + +.icon-following:before { + content: "\e6d4"; +} + +.icon-a-breaklink:before { + content: "\e6c5"; +} + +.icon-icon-beauty-smooth:before { + content: "\e64a"; +} + +.icon-icon-beauty-redness:before { + content: "\e64b"; +} + +.icon-icon-beauty-sharp:before { + content: "\e64c"; +} + +.icon-icon-beauty-whitening:before { + content: "\e64d"; +} + +.icon-a-volumedown:before { + content: "\e647"; +} + +.icon-a-volumemute:before { + content: "\e648"; +} + +.icon-a-volumeup:before { + content: "\e649"; +} + +.icon-emoji-animated:before { + content: "\e6c4"; +} + +.icon-flip:before { + content: "\e6e6"; +} + +.icon-mic-border:before { + content: "\e6e7"; +} + +.icon-a-randomplay:before { + content: "\e6e8"; +} + +.icon-speaker-border:before { + content: "\e6e9"; +} + +.icon-backfullscreen:before { + content: "\e6ca"; +} + +.icon-blocklisk:before { + content: "\e6ce"; +} + +.icon-beauty:before { + content: "\e6d1"; +} + +.icon-fullscreen-exit-fill:before { + content: "\e6d3"; +} + +.icon-fullscreen-fill:before { + content: "\e6d5"; +} + +.icon-fans:before { + content: "\e6d6"; +} + +.icon-fullscreen-mobile:before { + content: "\e6d7"; +} + +.icon-kick:before { + content: "\e6d8"; +} + +.icon-a-listplay:before { + content: "\e6d9"; +} + +.icon-previous:before { + content: "\e6da"; +} + +.icon-data:before { + content: "\e6db"; +} + +.icon-a-gifteffect:before { + content: "\e6dc"; +} + +.icon-gift-border:before { + content: "\e6dd"; +} + +.icon-a-loopplay:before { + content: "\e6de"; +} + +.icon-notice:before { + content: "\e6df"; +} + +.icon-time:before { + content: "\e6e0"; +} + +.icon-volume:before { + content: "\e6e1"; +} + +.icon-playlist:before { + content: "\e6e2"; +} + +.icon-next:before { + content: "\e6e3"; +} + +.icon-music:before { + content: "\e6e4"; +} + +.icon-offMic:before { + content: "\e6e5"; +} + +.icon-pause:before { + content: "\e6c1"; +} + +.icon-icon-fullImage:before { + content: "\e646"; +} + +.icon-icon-camera-fill:before { + content: "\e645"; +} + +.icon-vote:before { + content: "\e6c0"; +} + +.icon-icon-email:before { + content: "\e644"; +} + +.icon-icon_chatroom_bigmenu:before { + content: "\e674"; +} + +.icon-icon_chatroom_smallmenu:before { + content: "\e675"; +} + +.icon-icon_chatroom_mic:before { + content: "\e685"; +} + +.icon-icon_on:before { + content: "\e687"; +} + +.icon-icon_exit:before { + content: "\e688"; +} + +.icon-icon_chatroom_more:before { + content: "\e68f"; +} + +.icon-jinmai:before { + content: "\e694"; +} + +.icon-a-icon_shareinvtation:before { + content: "\e6ab"; +} + +.icon-icon_entrance:before { + content: "\e6bc"; +} + +.icon-icon_comunity_delete:before { + content: "\e643"; +} + +.icon-icon_comunity_reply:before { + content: "\e63e"; +} + +.icon-icon_community_like:before { + content: "\e63f"; +} + +.icon-icon_comunity_report:before { + content: "\e640"; +} + +.icon-icon_comunity_topics:before { + content: "\e641"; +} + +.icon-icon_comunity_comment:before { + content: "\e642"; +} + +.icon-icon_menu_Customer:before { + content: "\e6a4"; +} + +.icon-icon_post_collection:before { + content: "\e6c2"; +} + +.icon-icon_post_component:before { + content: "\e6c3"; +} + +.icon-icon_post_recommend:before { + content: "\e6c6"; +} + +.icon-icon_post_highlight:before { + content: "\e6c7"; +} + +.icon-icon_post_viewers:before { + content: "\e6c9"; +} + +.icon-icon_post_chitchat:before { + content: "\e6cc"; +} + +.icon-icon_post_selfies:before { + content: "\e6cd"; +} + +.icon-icon_post_official:before { + content: "\e6cb"; +} + +.icon-icon_order_tip:before { + content: "\e6d0"; +} + +.icon-icon_order_remark:before { + content: "\e6d2"; +} + +.icon-icon_refund:before { + content: "\e63c"; +} + +.icon-icon-post:before { + content: "\e63b"; +} + +.icon-backtop:before { + content: "\e63d"; +} + +.icon-faq:before { + content: "\e6ba"; +} + +.icon-onlineservice:before { + content: "\e6bb"; +} + +.icon-icon-vip:before { + content: "\e638"; +} + +.icon-icon-legends:before { + content: "\e639"; +} + +.icon-icon-influencer:before { + content: "\e63a"; +} + +.icon-icon-comment:before { + content: "\e636"; +} + +.icon-icon-helpCenter:before { + content: "\e634"; +} + +.icon-icon-setting:before { + content: "\e635"; +} + +.icon-icon-reload:before { + content: "\e633"; +} + +.icon-icon-reset:before { + content: "\e631"; +} + +.icon-icon-addCard:before { + content: "\e62e"; +} + +.icon-icon-send:before { + content: "\e630"; +} + +.icon-icon-service-fill:before { + content: "\e632"; +} + +.icon-icon-service:before { + content: "\e62f"; +} + +.icon-icon-random:before { + content: "\e62d"; +} + +.icon-icon-keyboard:before { + content: "\e62c"; +} + +.icon-icon-people:before { + content: "\e629"; +} + +.icon-icon_top:before { + content: "\e62b"; +} + +.icon-icon-order-fill:before { + content: "\e62a"; +} + +.icon-add:before { + content: "\e6b5"; +} + +.icon-reduce:before { + content: "\e6b6"; +} + +.icon-icon-contact:before { + content: "\e616"; +} + +.icon-Warning:before { + content: "\e614"; +} + +.icon-icon_im_purpleface:before { + content: "\e6bd"; +} + +.icon-icon_im_emoji:before { + content: "\e6bf"; +} + +.icon-icon_im_other:before { + content: "\e6cf"; +} + +.icon-msgmute:before { + content: "\e686"; +} + +.icon-block:before { + content: "\e6b9"; +} + +.icon-mute:before { + content: "\e684"; +} + +.icon-open-mic:before { + content: "\e6b4"; +} + +.icon-hang-up:before { + content: "\e68d"; +} + +.icon-emoji:before { + content: "\e693"; +} + +.icon-quote:before { + content: "\e6b3"; +} + +.icon-checked:before { + content: "\e6b2"; +} + +.icon-report:before { + content: "\e6b1"; +} + +.icon-trashcan:before { + content: "\e612"; +} + +.icon-public:before { + content: "\e698"; +} + +.icon-private:before { + content: "\e699"; +} + +.icon-uploadimg:before { + content: "\e69c"; +} + +.icon-uploadclip:before { + content: "\e69d"; +} + +.icon-arrow-down-border:before { + content: "\e6af"; +} + +.icon-arrow-left:before { + content: "\e6b0"; +} + +.icon-eye-off:before { + content: "\e6ae"; +} + +.icon-eye-on:before { + content: "\e6ad"; +} + +.icon-arrow-down-fill:before { + content: "\e6ac"; +} + +.icon-arrow-left-border:before { + content: "\e6aa"; +} + +.icon-delete-02:before { + content: "\e628"; +} + +.icon-arrow-down-circle:before { + content: "\e601"; +} + +.icon-arrow-right-border:before { + content: "\e602"; +} + +.icon-arrow-up-fill:before { + content: "\e603"; +} + +.icon-arrow-right:before { + content: "\e604"; +} + +.icon-Chat:before { + content: "\e605"; +} + +.icon-close:before { + content: "\e607"; +} + +.icon-a-Createroom:before { + content: "\e608"; +} + +.icon-Call:before { + content: "\e609"; +} + +.icon-Like-fill:before { + content: "\e60a"; +} + +.icon-filter-fill:before { + content: "\e60b"; +} + +.icon-live:before { + content: "\e60c"; +} + +.icon-follow:before { + content: "\e60d"; +} + +.icon-Play:before { + content: "\e60e"; +} + +.icon-events:before { + content: "\e60f"; +} + +.icon-More:before { + content: "\e610"; +} + +.icon-matching:before { + content: "\e611"; +} + +.icon-Like:before { + content: "\e613"; +} + +.icon-loading:before { + content: "\e615"; +} + +.icon-Search:before { + content: "\e617"; +} + +.icon-filter:before { + content: "\e618"; +} + +.icon-social-facebook:before { + content: "\e619"; +} + +.icon-select:before { + content: "\e61a"; +} + +.icon-Share-border:before { + content: "\e61b"; +} + +.icon-social-instagram:before { + content: "\e61c"; +} + +.icon-social-twitch:before { + content: "\e61d"; +} + +.icon-Voice:before { + content: "\e61e"; +} + +.icon-social-tiktok:before { + content: "\e61f"; +} + +.icon-social-google:before { + content: "\e620"; +} + +.icon-social-youtube:before { + content: "\e621"; +} + +.icon-social-apple:before { + content: "\e622"; +} + +.icon-social-twitter:before { + content: "\e623"; +} + +.icon-star-fill:before { + content: "\e624"; +} + +.icon-star:before { + content: "\e625"; +} + +.icon-social-discord:before { + content: "\e626"; +} + +.icon-messages:before { + content: "\e627"; +} + diff --git a/public/font/iconfont.js b/public/font/iconfont.js new file mode 100644 index 0000000..1eef2a5 --- /dev/null +++ b/public/font/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_4975639='',(c=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,h,i,m,t,v=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_4975639,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?v(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),o()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(i=o,m=c.document,t=!1,s(),m.onreadystatechange=function(){"complete"==m.readyState&&(m.onreadystatechange=null,z())})}function z(){t||(t=!0,i())}function s(){try{m.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}z()}})(window); \ No newline at end of file diff --git a/public/font/iconfont.json b/public/font/iconfont.json new file mode 100644 index 0000000..9e5cf34 --- /dev/null +++ b/public/font/iconfont.json @@ -0,0 +1,1437 @@ +{ + "id": "4975639", + "name": "CrushLevel", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "45504317", + "name": "shield", + "font_class": "shield", + "unicode": "e6fa", + "unicode_decimal": 59130 + }, + { + "icon_id": "45504316", + "name": "audits", + "font_class": "audits", + "unicode": "e6fb", + "unicode_decimal": 59131 + }, + { + "icon_id": "45330662", + "name": "post_recommend_fill", + "font_class": "post_recommend_fill", + "unicode": "e6f9", + "unicode_decimal": 59129 + }, + { + "icon_id": "45330650", + "name": "post_Notrecommend_fill", + "font_class": "post_Notrecommend_fill", + "unicode": "e6f8", + "unicode_decimal": 59128 + }, + { + "icon_id": "45244863", + "name": "voice_msg", + "font_class": "voice_msg", + "unicode": "e652", + "unicode_decimal": 58962 + }, + { + "icon_id": "45244862", + "name": "clear", + "font_class": "clear", + "unicode": "e657", + "unicode_decimal": 58967 + }, + { + "icon_id": "45244861", + "name": "prompt", + "font_class": "prompt", + "unicode": "e66f", + "unicode_decimal": 58991 + }, + { + "icon_id": "44860023", + "name": "statistics", + "font_class": "statistics", + "unicode": "e66e", + "unicode_decimal": 58990 + }, + { + "icon_id": "44513036", + "name": "sort_az", + "font_class": "sort_az", + "unicode": "e66d", + "unicode_decimal": 58989 + }, + { + "icon_id": "44124600", + "name": "orderlobby", + "font_class": "orderlobby", + "unicode": "e6f7", + "unicode_decimal": 59127 + }, + { + "icon_id": "44005343", + "name": "link", + "font_class": "link", + "unicode": "e6f6", + "unicode_decimal": 59126 + }, + { + "icon_id": "43913018", + "name": "minimize", + "font_class": "minimize", + "unicode": "e66c", + "unicode_decimal": 58988 + }, + { + "icon_id": "43909016", + "name": "icon_watchtogether", + "font_class": "icon_watchtogether", + "unicode": "e6f5", + "unicode_decimal": 59125 + }, + { + "icon_id": "43695294", + "name": "seat", + "font_class": "seat", + "unicode": "e6f4", + "unicode_decimal": 59124 + }, + { + "icon_id": "43665887", + "name": "mute all", + "font_class": "mute1", + "unicode": "e66b", + "unicode_decimal": 58987 + }, + { + "icon_id": "43616417", + "name": "entrance sound", + "font_class": "a-entrancesound", + "unicode": "e6f3", + "unicode_decimal": 59123 + }, + { + "icon_id": "42073284", + "name": "sort", + "font_class": "sort", + "unicode": "e66a", + "unicode_decimal": 58986 + }, + { + "icon_id": "41814687", + "name": "VIP_nocolor", + "font_class": "VIP_nocolor", + "unicode": "e6f2", + "unicode_decimal": 59122 + }, + { + "icon_id": "41246214", + "name": "purchaseorder", + "font_class": "purchaseorder", + "unicode": "e6f0", + "unicode_decimal": 59120 + }, + { + "icon_id": "41246213", + "name": "placeorder", + "font_class": "placeorder", + "unicode": "e6f1", + "unicode_decimal": 59121 + }, + { + "icon_id": "41066355", + "name": "connect", + "font_class": "connect", + "unicode": "e669", + "unicode_decimal": 58985 + }, + { + "icon_id": "41045990", + "name": "tag", + "font_class": "tag", + "unicode": "e668", + "unicode_decimal": 58984 + }, + { + "icon_id": "40519075", + "name": "card", + "font_class": "card", + "unicode": "e667", + "unicode_decimal": 58983 + }, + { + "icon_id": "40514741", + "name": "Steam Deck", + "font_class": "Steam", + "unicode": "e666", + "unicode_decimal": 58982 + }, + { + "icon_id": "40482759", + "name": "Android", + "font_class": "Android", + "unicode": "e661", + "unicode_decimal": 58977 + }, + { + "icon_id": "40482753", + "name": "Xbox", + "font_class": "Xbox", + "unicode": "e662", + "unicode_decimal": 58978 + }, + { + "icon_id": "40482751", + "name": "PS", + "font_class": "PS", + "unicode": "e663", + "unicode_decimal": 58979 + }, + { + "icon_id": "40482752", + "name": "Switch", + "font_class": "Switch", + "unicode": "e664", + "unicode_decimal": 58980 + }, + { + "icon_id": "40482750", + "name": "PC", + "font_class": "PC", + "unicode": "e665", + "unicode_decimal": 58981 + }, + { + "icon_id": "39973943", + "name": "thread", + "font_class": "thread", + "unicode": "e6ef", + "unicode_decimal": 59119 + }, + { + "icon_id": "39478612", + "name": "icon_post_notrecommend", + "font_class": "icon_post_Notrecommend", + "unicode": "e6ee", + "unicode_decimal": 59118 + }, + { + "icon_id": "38204830", + "name": "coupon-border", + "font_class": "coupon-border", + "unicode": "e660", + "unicode_decimal": 58976 + }, + { + "icon_id": "38160929", + "name": "date", + "font_class": "date", + "unicode": "e6ed", + "unicode_decimal": 59117 + }, + { + "icon_id": "37232886", + "name": "mic request border", + "font_class": "a-micrequestborder", + "unicode": "e65e", + "unicode_decimal": 58974 + }, + { + "icon_id": "37232885", + "name": "mic request", + "font_class": "a-micrequest", + "unicode": "e65f", + "unicode_decimal": 58975 + }, + { + "icon_id": "37060062", + "name": "icon-public-border", + "font_class": "public-border", + "unicode": "e65c", + "unicode_decimal": 58972 + }, + { + "icon_id": "37060061", + "name": "icon-private-border", + "font_class": "private-border", + "unicode": "e65d", + "unicode_decimal": 58973 + }, + { + "icon_id": "35589811", + "name": "female", + "font_class": "female", + "unicode": "e659", + "unicode_decimal": 58969 + }, + { + "icon_id": "35589812", + "name": "nonconforming", + "font_class": "nonconforming", + "unicode": "e65a", + "unicode_decimal": 58970 + }, + { + "icon_id": "35589813", + "name": "male", + "font_class": "male", + "unicode": "e65b", + "unicode_decimal": 58971 + }, + { + "icon_id": "34611227", + "name": "icon_exclamation", + "font_class": "icon_hint", + "unicode": "e6ec", + "unicode_decimal": 59116 + }, + { + "icon_id": "34303845", + "name": "stop", + "font_class": "stop", + "unicode": "e658", + "unicode_decimal": 58968 + }, + { + "icon_id": "34084643", + "name": "language", + "font_class": "language", + "unicode": "e654", + "unicode_decimal": 58964 + }, + { + "icon_id": "34084644", + "name": "account", + "font_class": "account", + "unicode": "e655", + "unicode_decimal": 58965 + }, + { + "icon_id": "34084645", + "name": "IM-setting", + "font_class": "IM-setting", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "33241723", + "name": "info", + "font_class": "info", + "unicode": "e653", + "unicode_decimal": 58963 + }, + { + "icon_id": "33230167", + "name": "copy", + "font_class": "copy", + "unicode": "e6eb", + "unicode_decimal": 59115 + }, + { + "icon_id": "33070007", + "name": "icon-drag", + "font_class": "icon-drag", + "unicode": "e650", + "unicode_decimal": 58960 + }, + { + "icon_id": "33052223", + "name": "Invite to mic", + "font_class": "a-Invitetomic", + "unicode": "e6ea", + "unicode_decimal": 59114 + }, + { + "icon_id": "33043465", + "name": "voice-live", + "font_class": "voice-live", + "unicode": "e651", + "unicode_decimal": 58961 + }, + { + "icon_id": "32983364", + "name": "question", + "font_class": "question", + "unicode": "e64f", + "unicode_decimal": 58959 + }, + { + "icon_id": "32935983", + "name": "start-live", + "font_class": "a-createlive", + "unicode": "e64e", + "unicode_decimal": 58958 + }, + { + "icon_id": "32963380", + "name": "unfollow", + "font_class": "unfollow", + "unicode": "e6c8", + "unicode_decimal": 59080 + }, + { + "icon_id": "32963381", + "name": "following", + "font_class": "following", + "unicode": "e6d4", + "unicode_decimal": 59092 + }, + { + "icon_id": "32939490", + "name": "break link", + "font_class": "a-breaklink", + "unicode": "e6c5", + "unicode_decimal": 59077 + }, + { + "icon_id": "32931442", + "name": "icon-beauty-smooth", + "font_class": "icon-beauty-smooth", + "unicode": "e64a", + "unicode_decimal": 58954 + }, + { + "icon_id": "32931443", + "name": "icon-beauty-redness", + "font_class": "icon-beauty-redness", + "unicode": "e64b", + "unicode_decimal": 58955 + }, + { + "icon_id": "32931444", + "name": "icon-beauty-sharp", + "font_class": "icon-beauty-sharp", + "unicode": "e64c", + "unicode_decimal": 58956 + }, + { + "icon_id": "32931445", + "name": "icon-beauty-whitening", + "font_class": "icon-beauty-whitening", + "unicode": "e64d", + "unicode_decimal": 58957 + }, + { + "icon_id": "32926134", + "name": "volume down", + "font_class": "a-volumedown", + "unicode": "e647", + "unicode_decimal": 58951 + }, + { + "icon_id": "32926135", + "name": "volume mute", + "font_class": "a-volumemute", + "unicode": "e648", + "unicode_decimal": 58952 + }, + { + "icon_id": "32926136", + "name": "volume up", + "font_class": "a-volumeup", + "unicode": "e649", + "unicode_decimal": 58953 + }, + { + "icon_id": "32918681", + "name": "emoji-animated", + "font_class": "emoji-animated", + "unicode": "e6c4", + "unicode_decimal": 59076 + }, + { + "icon_id": "32867002", + "name": "flip", + "font_class": "flip", + "unicode": "e6e6", + "unicode_decimal": 59110 + }, + { + "icon_id": "32867003", + "name": "mic-border", + "font_class": "mic-border", + "unicode": "e6e7", + "unicode_decimal": 59111 + }, + { + "icon_id": "32867004", + "name": "random play", + "font_class": "a-randomplay", + "unicode": "e6e8", + "unicode_decimal": 59112 + }, + { + "icon_id": "32867079", + "name": "speaker-border", + "font_class": "speaker-border", + "unicode": "e6e9", + "unicode_decimal": 59113 + }, + { + "icon_id": "32866980", + "name": "backfullscreen", + "font_class": "backfullscreen", + "unicode": "e6ca", + "unicode_decimal": 59082 + }, + { + "icon_id": "32866981", + "name": "blocklisk", + "font_class": "blocklisk", + "unicode": "e6ce", + "unicode_decimal": 59086 + }, + { + "icon_id": "32866982", + "name": "beauty", + "font_class": "beauty", + "unicode": "e6d1", + "unicode_decimal": 59089 + }, + { + "icon_id": "32866983", + "name": "fullscreen-exit-fill", + "font_class": "fullscreen-exit-fill", + "unicode": "e6d3", + "unicode_decimal": 59091 + }, + { + "icon_id": "32866985", + "name": "fullscreen-fill", + "font_class": "fullscreen-fill", + "unicode": "e6d5", + "unicode_decimal": 59093 + }, + { + "icon_id": "32866986", + "name": "fans", + "font_class": "fans", + "unicode": "e6d6", + "unicode_decimal": 59094 + }, + { + "icon_id": "32866987", + "name": "fullscreen-mobile", + "font_class": "fullscreen-mobile", + "unicode": "e6d7", + "unicode_decimal": 59095 + }, + { + "icon_id": "32866988", + "name": "kick", + "font_class": "kick", + "unicode": "e6d8", + "unicode_decimal": 59096 + }, + { + "icon_id": "32866989", + "name": "list play", + "font_class": "a-listplay", + "unicode": "e6d9", + "unicode_decimal": 59097 + }, + { + "icon_id": "32866990", + "name": "previous", + "font_class": "previous", + "unicode": "e6da", + "unicode_decimal": 59098 + }, + { + "icon_id": "32866991", + "name": "data", + "font_class": "data", + "unicode": "e6db", + "unicode_decimal": 59099 + }, + { + "icon_id": "32866992", + "name": "gift effect", + "font_class": "a-gifteffect", + "unicode": "e6dc", + "unicode_decimal": 59100 + }, + { + "icon_id": "32866993", + "name": "gift-border", + "font_class": "gift-border", + "unicode": "e6dd", + "unicode_decimal": 59101 + }, + { + "icon_id": "32866994", + "name": "loop play", + "font_class": "a-loopplay", + "unicode": "e6de", + "unicode_decimal": 59102 + }, + { + "icon_id": "32866995", + "name": "notice", + "font_class": "notice", + "unicode": "e6df", + "unicode_decimal": 59103 + }, + { + "icon_id": "32866996", + "name": "time", + "font_class": "time", + "unicode": "e6e0", + "unicode_decimal": 59104 + }, + { + "icon_id": "32866997", + "name": "volume", + "font_class": "volume", + "unicode": "e6e1", + "unicode_decimal": 59105 + }, + { + "icon_id": "32866998", + "name": "playlist", + "font_class": "playlist", + "unicode": "e6e2", + "unicode_decimal": 59106 + }, + { + "icon_id": "32866999", + "name": "next", + "font_class": "next", + "unicode": "e6e3", + "unicode_decimal": 59107 + }, + { + "icon_id": "32867000", + "name": "music", + "font_class": "music", + "unicode": "e6e4", + "unicode_decimal": 59108 + }, + { + "icon_id": "32867001", + "name": "offMic", + "font_class": "offMic", + "unicode": "e6e5", + "unicode_decimal": 59109 + }, + { + "icon_id": "32865120", + "name": "pause", + "font_class": "pause", + "unicode": "e6c1", + "unicode_decimal": 59073 + }, + { + "icon_id": "32511005", + "name": "icon-fullImage", + "font_class": "icon-fullImage", + "unicode": "e646", + "unicode_decimal": 58950 + }, + { + "icon_id": "32447489", + "name": "icon-camera-fill", + "font_class": "icon-camera-fill", + "unicode": "e645", + "unicode_decimal": 58949 + }, + { + "icon_id": "32097519", + "name": "vote", + "font_class": "vote", + "unicode": "e6c0", + "unicode_decimal": 59072 + }, + { + "icon_id": "32102567", + "name": "icon-email", + "font_class": "icon-email", + "unicode": "e644", + "unicode_decimal": 58948 + }, + { + "icon_id": "17479540", + "name": "icon_chatroom_bigmenu", + "font_class": "icon_chatroom_bigmenu", + "unicode": "e674", + "unicode_decimal": 58996 + }, + { + "icon_id": "17479541", + "name": "icon_chatroom_smallmenu", + "font_class": "icon_chatroom_smallmenu", + "unicode": "e675", + "unicode_decimal": 58997 + }, + { + "icon_id": "18249546", + "name": "chatroom_mic", + "font_class": "icon_chatroom_mic", + "unicode": "e685", + "unicode_decimal": 59013 + }, + { + "icon_id": "18249552", + "name": "chatroom_on", + "font_class": "icon_on", + "unicode": "e687", + "unicode_decimal": 59015 + }, + { + "icon_id": "18249568", + "name": "exit", + "font_class": "icon_exit", + "unicode": "e688", + "unicode_decimal": 59016 + }, + { + "icon_id": "18294099", + "name": "chatroom_more", + "font_class": "icon_chatroom_more", + "unicode": "e68f", + "unicode_decimal": 59023 + }, + { + "icon_id": "18594409", + "name": "icon_fobidden", + "font_class": "jinmai", + "unicode": "e694", + "unicode_decimal": 59028 + }, + { + "icon_id": "23067626", + "name": "icon_share invtation", + "font_class": "a-icon_shareinvtation", + "unicode": "e6ab", + "unicode_decimal": 59051 + }, + { + "icon_id": "23771600", + "name": "ICON_entrance", + "font_class": "icon_entrance", + "unicode": "e6bc", + "unicode_decimal": 59068 + }, + { + "icon_id": "13968079", + "name": "icon_comunity_delete", + "font_class": "icon_comunity_delete", + "unicode": "e643", + "unicode_decimal": 58947 + }, + { + "icon_id": "13968080", + "name": "icon_comunity_reply", + "font_class": "icon_comunity_reply", + "unicode": "e63e", + "unicode_decimal": 58942 + }, + { + "icon_id": "13968081", + "name": "icon_community_like", + "font_class": "icon_community_like", + "unicode": "e63f", + "unicode_decimal": 58943 + }, + { + "icon_id": "13968082", + "name": "icon_comunity_report", + "font_class": "icon_comunity_report", + "unicode": "e640", + "unicode_decimal": 58944 + }, + { + "icon_id": "13968083", + "name": "icon_comunity_topics", + "font_class": "icon_comunity_topics", + "unicode": "e641", + "unicode_decimal": 58945 + }, + { + "icon_id": "13968084", + "name": "icon_comunity_comment", + "font_class": "icon_comunity_comment", + "unicode": "e642", + "unicode_decimal": 58946 + }, + { + "icon_id": "20241151", + "name": "icon_menu_Customer", + "font_class": "icon_menu_Customer", + "unicode": "e6a4", + "unicode_decimal": 59044 + }, + { + "icon_id": "26233594", + "name": "icon_post_collection", + "font_class": "icon_post_collection", + "unicode": "e6c2", + "unicode_decimal": 59074 + }, + { + "icon_id": "26233595", + "name": "icon_post_component", + "font_class": "icon_post_component", + "unicode": "e6c3", + "unicode_decimal": 59075 + }, + { + "icon_id": "26233598", + "name": "icon_post_recommend", + "font_class": "icon_post_recommend", + "unicode": "e6c6", + "unicode_decimal": 59078 + }, + { + "icon_id": "26233599", + "name": "icon_post_highlight", + "font_class": "icon_post_highlight", + "unicode": "e6c7", + "unicode_decimal": 59079 + }, + { + "icon_id": "26363855", + "name": "icon_post_viewers", + "font_class": "icon_post_viewers", + "unicode": "e6c9", + "unicode_decimal": 59081 + }, + { + "icon_id": "27543804", + "name": "icon_post_chitchat", + "font_class": "icon_post_chitchat", + "unicode": "e6cc", + "unicode_decimal": 59084 + }, + { + "icon_id": "27543805", + "name": "icon_post_selfies", + "font_class": "icon_post_selfies", + "unicode": "e6cd", + "unicode_decimal": 59085 + }, + { + "icon_id": "28503033", + "name": "icon_post_official", + "font_class": "icon_post_official", + "unicode": "e6cb", + "unicode_decimal": 59083 + }, + { + "icon_id": "27983298", + "name": "icon_order_tip", + "font_class": "icon_order_tip", + "unicode": "e6d0", + "unicode_decimal": 59088 + }, + { + "icon_id": "30038315", + "name": "icon_order_remark", + "font_class": "icon_order_remark", + "unicode": "e6d2", + "unicode_decimal": 59090 + }, + { + "icon_id": "12908387", + "name": "icon_refund", + "font_class": "icon_refund", + "unicode": "e63c", + "unicode_decimal": 58940 + }, + { + "icon_id": "32073072", + "name": "icon-post", + "font_class": "icon-post", + "unicode": "e63b", + "unicode_decimal": 58939 + }, + { + "icon_id": "14348065", + "name": "backtop", + "font_class": "backtop", + "unicode": "e63d", + "unicode_decimal": 58941 + }, + { + "icon_id": "32058809", + "name": "faq", + "font_class": "faq", + "unicode": "e6ba", + "unicode_decimal": 59066 + }, + { + "icon_id": "32058810", + "name": "onlineservice", + "font_class": "onlineservice", + "unicode": "e6bb", + "unicode_decimal": 59067 + }, + { + "icon_id": "32020329", + "name": "icon-vip", + "font_class": "icon-vip", + "unicode": "e638", + "unicode_decimal": 58936 + }, + { + "icon_id": "32020330", + "name": "icon-legends", + "font_class": "icon-legends", + "unicode": "e639", + "unicode_decimal": 58937 + }, + { + "icon_id": "32020331", + "name": "icon-influencer", + "font_class": "icon-influencer", + "unicode": "e63a", + "unicode_decimal": 58938 + }, + { + "icon_id": "32017893", + "name": "icon-comment", + "font_class": "icon-comment", + "unicode": "e636", + "unicode_decimal": 58934 + }, + { + "icon_id": "32017819", + "name": "icon-helpCenter", + "font_class": "icon-helpCenter", + "unicode": "e634", + "unicode_decimal": 58932 + }, + { + "icon_id": "32017822", + "name": "icon-setting", + "font_class": "icon-setting", + "unicode": "e635", + "unicode_decimal": 58933 + }, + { + "icon_id": "32017700", + "name": "icon-reload", + "font_class": "icon-reload", + "unicode": "e633", + "unicode_decimal": 58931 + }, + { + "icon_id": "32006043", + "name": "icon-reset", + "font_class": "icon-reset", + "unicode": "e631", + "unicode_decimal": 58929 + }, + { + "icon_id": "31957204", + "name": "icon-addCard", + "font_class": "icon-addCard", + "unicode": "e62e", + "unicode_decimal": 58926 + }, + { + "icon_id": "31957205", + "name": "icon-send", + "font_class": "icon-send", + "unicode": "e630", + "unicode_decimal": 58928 + }, + { + "icon_id": "31957611", + "name": "icon-service-fill", + "font_class": "icon-service-fill", + "unicode": "e632", + "unicode_decimal": 58930 + }, + { + "icon_id": "31956004", + "name": "icon-service", + "font_class": "icon-service", + "unicode": "e62f", + "unicode_decimal": 58927 + }, + { + "icon_id": "31946118", + "name": "icon-random", + "font_class": "icon-random", + "unicode": "e62d", + "unicode_decimal": 58925 + }, + { + "icon_id": "31937875", + "name": "icon-keyboard", + "font_class": "icon-keyboard", + "unicode": "e62c", + "unicode_decimal": 58924 + }, + { + "icon_id": "31937869", + "name": "icon-people", + "font_class": "icon-people", + "unicode": "e629", + "unicode_decimal": 58921 + }, + { + "icon_id": "31937870", + "name": "icon_top", + "font_class": "icon_top", + "unicode": "e62b", + "unicode_decimal": 58923 + }, + { + "icon_id": "31937078", + "name": "icon-order-fill", + "font_class": "icon-order-fill", + "unicode": "e62a", + "unicode_decimal": 58922 + }, + { + "icon_id": "31933275", + "name": "add", + "font_class": "add", + "unicode": "e6b5", + "unicode_decimal": 59061 + }, + { + "icon_id": "31933276", + "name": "reduce", + "font_class": "reduce", + "unicode": "e6b6", + "unicode_decimal": 59062 + }, + { + "icon_id": "31916329", + "name": "icon-contact", + "font_class": "icon-contact", + "unicode": "e616", + "unicode_decimal": 58902 + }, + { + "icon_id": "31915902", + "name": "Warning", + "font_class": "Warning", + "unicode": "e614", + "unicode_decimal": 58900 + }, + { + "icon_id": "25786453", + "name": "icon_im_purpleface", + "font_class": "icon_im_purpleface", + "unicode": "e6bd", + "unicode_decimal": 59069 + }, + { + "icon_id": "25786454", + "name": "icon_im_emoji", + "font_class": "icon_im_emoji", + "unicode": "e6bf", + "unicode_decimal": 59071 + }, + { + "icon_id": "27686233", + "name": "icon_im_other", + "font_class": "icon_im_other", + "unicode": "e6cf", + "unicode_decimal": 59087 + }, + { + "icon_id": "18249550", + "name": "chatroom_off", + "font_class": "msgmute", + "unicode": "e686", + "unicode_decimal": 59014 + }, + { + "icon_id": "24535102", + "name": "icon_IM_blocked", + "font_class": "block", + "unicode": "e6b9", + "unicode_decimal": 59065 + }, + { + "icon_id": "18255393", + "name": "icon_call_mute", + "font_class": "mute", + "unicode": "e684", + "unicode_decimal": 59012 + }, + { + "icon_id": "23525940", + "name": "icon_call_openmic", + "font_class": "open-mic", + "unicode": "e6b4", + "unicode_decimal": 59060 + }, + { + "icon_id": "18255394", + "name": "icon_call_hangup", + "font_class": "hang-up", + "unicode": "e68d", + "unicode_decimal": 59021 + }, + { + "icon_id": "18529424", + "name": "icon_interact", + "font_class": "emoji", + "unicode": "e693", + "unicode_decimal": 59027 + }, + { + "icon_id": "31882851", + "name": "quote", + "font_class": "quote", + "unicode": "e6b3", + "unicode_decimal": 59059 + }, + { + "icon_id": "31882806", + "name": "checked", + "font_class": "checked", + "unicode": "e6b2", + "unicode_decimal": 59058 + }, + { + "icon_id": "31882794", + "name": "report", + "font_class": "report", + "unicode": "e6b1", + "unicode_decimal": 59057 + }, + { + "icon_id": "12908372", + "name": "icon_delete", + "font_class": "trashcan", + "unicode": "e612", + "unicode_decimal": 58898 + }, + { + "icon_id": "19500607", + "name": "icon_public", + "font_class": "public", + "unicode": "e698", + "unicode_decimal": 59032 + }, + { + "icon_id": "19500711", + "name": "icon_private", + "font_class": "private", + "unicode": "e699", + "unicode_decimal": 59033 + }, + { + "icon_id": "19675398", + "name": "icon_uploadimg", + "font_class": "uploadimg", + "unicode": "e69c", + "unicode_decimal": 59036 + }, + { + "icon_id": "19675399", + "name": "icon_uploadclip", + "font_class": "uploadclip", + "unicode": "e69d", + "unicode_decimal": 59037 + }, + { + "icon_id": "31882415", + "name": "arrow-down-border", + "font_class": "arrow-down-border", + "unicode": "e6af", + "unicode_decimal": 59055 + }, + { + "icon_id": "31882430", + "name": "arrow-left", + "font_class": "arrow-left", + "unicode": "e6b0", + "unicode_decimal": 59056 + }, + { + "icon_id": "31882412", + "name": "eye-off", + "font_class": "eye-off", + "unicode": "e6ae", + "unicode_decimal": 59054 + }, + { + "icon_id": "31882406", + "name": "eye-on", + "font_class": "eye-on", + "unicode": "e6ad", + "unicode_decimal": 59053 + }, + { + "icon_id": "31818154", + "name": "arrow-down-fill", + "font_class": "arrow-down-fill", + "unicode": "e6ac", + "unicode_decimal": 59052 + }, + { + "icon_id": "31818069", + "name": "arrow-left-border", + "font_class": "arrow-left-border", + "unicode": "e6aa", + "unicode_decimal": 59050 + }, + { + "icon_id": "31801946", + "name": "delete-02", + "font_class": "delete-02", + "unicode": "e628", + "unicode_decimal": 58920 + }, + { + "icon_id": "31801045", + "name": "arrow-down-circle", + "font_class": "arrow-down-circle", + "unicode": "e601", + "unicode_decimal": 58881 + }, + { + "icon_id": "31801046", + "name": "arrow-right-border", + "font_class": "arrow-right-border", + "unicode": "e602", + "unicode_decimal": 58882 + }, + { + "icon_id": "31801047", + "name": "arrow-up-fill", + "font_class": "arrow-up-fill", + "unicode": "e603", + "unicode_decimal": 58883 + }, + { + "icon_id": "31801048", + "name": "arrow-right", + "font_class": "arrow-right", + "unicode": "e604", + "unicode_decimal": 58884 + }, + { + "icon_id": "31801049", + "name": "Chat", + "font_class": "Chat", + "unicode": "e605", + "unicode_decimal": 58885 + }, + { + "icon_id": "31801051", + "name": "delete", + "font_class": "close", + "unicode": "e607", + "unicode_decimal": 58887 + }, + { + "icon_id": "31801052", + "name": "Create room", + "font_class": "a-Createroom", + "unicode": "e608", + "unicode_decimal": 58888 + }, + { + "icon_id": "31801053", + "name": "Call", + "font_class": "Call", + "unicode": "e609", + "unicode_decimal": 58889 + }, + { + "icon_id": "31801054", + "name": "Like-fill", + "font_class": "Like-fill", + "unicode": "e60a", + "unicode_decimal": 58890 + }, + { + "icon_id": "31801055", + "name": "filter-fill", + "font_class": "filter-fill", + "unicode": "e60b", + "unicode_decimal": 58891 + }, + { + "icon_id": "31801056", + "name": "live", + "font_class": "live", + "unicode": "e60c", + "unicode_decimal": 58892 + }, + { + "icon_id": "31801057", + "name": "follow", + "font_class": "follow", + "unicode": "e60d", + "unicode_decimal": 58893 + }, + { + "icon_id": "31801058", + "name": "Play", + "font_class": "Play", + "unicode": "e60e", + "unicode_decimal": 58894 + }, + { + "icon_id": "31801059", + "name": "events", + "font_class": "events", + "unicode": "e60f", + "unicode_decimal": 58895 + }, + { + "icon_id": "31801060", + "name": "More", + "font_class": "More", + "unicode": "e610", + "unicode_decimal": 58896 + }, + { + "icon_id": "31801061", + "name": "matching", + "font_class": "matching", + "unicode": "e611", + "unicode_decimal": 58897 + }, + { + "icon_id": "31801063", + "name": "Like", + "font_class": "Like", + "unicode": "e613", + "unicode_decimal": 58899 + }, + { + "icon_id": "31801065", + "name": "loading", + "font_class": "loading", + "unicode": "e615", + "unicode_decimal": 58901 + }, + { + "icon_id": "31801067", + "name": "Search", + "font_class": "Search", + "unicode": "e617", + "unicode_decimal": 58903 + }, + { + "icon_id": "31801068", + "name": "filter", + "font_class": "filter", + "unicode": "e618", + "unicode_decimal": 58904 + }, + { + "icon_id": "31801069", + "name": "social-facebook", + "font_class": "social-facebook", + "unicode": "e619", + "unicode_decimal": 58905 + }, + { + "icon_id": "31801070", + "name": "select", + "font_class": "select", + "unicode": "e61a", + "unicode_decimal": 58906 + }, + { + "icon_id": "31801071", + "name": "Share-border", + "font_class": "Share-border", + "unicode": "e61b", + "unicode_decimal": 58907 + }, + { + "icon_id": "31801072", + "name": "social-instagram", + "font_class": "social-instagram", + "unicode": "e61c", + "unicode_decimal": 58908 + }, + { + "icon_id": "31801073", + "name": "social-twitch", + "font_class": "social-twitch", + "unicode": "e61d", + "unicode_decimal": 58909 + }, + { + "icon_id": "31801074", + "name": "Voice", + "font_class": "Voice", + "unicode": "e61e", + "unicode_decimal": 58910 + }, + { + "icon_id": "31801075", + "name": "social-tiktok", + "font_class": "social-tiktok", + "unicode": "e61f", + "unicode_decimal": 58911 + }, + { + "icon_id": "31801076", + "name": "social-google", + "font_class": "social-google", + "unicode": "e620", + "unicode_decimal": 58912 + }, + { + "icon_id": "31801077", + "name": "social-youtube", + "font_class": "social-youtube", + "unicode": "e621", + "unicode_decimal": 58913 + }, + { + "icon_id": "31801078", + "name": "social-apple", + "font_class": "social-apple", + "unicode": "e622", + "unicode_decimal": 58914 + }, + { + "icon_id": "31801079", + "name": "icon-kfckfc", + "font_class": "social-twitter", + "unicode": "e623", + "unicode_decimal": 58915 + }, + { + "icon_id": "31801080", + "name": "star-fill", + "font_class": "star-fill", + "unicode": "e624", + "unicode_decimal": 58916 + }, + { + "icon_id": "31801081", + "name": "star", + "font_class": "star", + "unicode": "e625", + "unicode_decimal": 58917 + }, + { + "icon_id": "31801082", + "name": "social-discord", + "font_class": "social-discord", + "unicode": "e626", + "unicode_decimal": 58918 + }, + { + "icon_id": "31801083", + "name": "messages", + "font_class": "messages", + "unicode": "e627", + "unicode_decimal": 58919 + } + ] +} diff --git a/public/font/iconfont.ttf b/public/font/iconfont.ttf new file mode 100644 index 0000000..0d6f53b Binary files /dev/null and b/public/font/iconfont.ttf differ diff --git a/public/font/iconfont.woff b/public/font/iconfont.woff new file mode 100644 index 0000000..41f1719 Binary files /dev/null and b/public/font/iconfont.woff differ diff --git a/public/font/iconfont.woff2 b/public/font/iconfont.woff2 new file mode 100644 index 0000000..7f06f5c Binary files /dev/null and b/public/font/iconfont.woff2 differ diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/buy-times.png b/public/icons/buy-times.png new file mode 100644 index 0000000..27c73a1 Binary files /dev/null and b/public/icons/buy-times.png differ diff --git a/public/icons/contact-heart.svg b/public/icons/contact-heart.svg new file mode 100644 index 0000000..aa5b3b8 --- /dev/null +++ b/public/icons/contact-heart.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/contact.svg b/public/icons/contact.svg new file mode 100644 index 0000000..5ab5d48 --- /dev/null +++ b/public/icons/contact.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/icons/contact_selected.svg b/public/icons/contact_selected.svg new file mode 100644 index 0000000..19fc302 --- /dev/null +++ b/public/icons/contact_selected.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/copyright-rule.svg b/public/icons/copyright-rule.svg new file mode 100644 index 0000000..825e956 --- /dev/null +++ b/public/icons/copyright-rule.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/create.svg b/public/icons/create.svg new file mode 100644 index 0000000..2fcc820 --- /dev/null +++ b/public/icons/create.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/icons/create_selected.svg b/public/icons/create_selected.svg new file mode 100644 index 0000000..dad8544 --- /dev/null +++ b/public/icons/create_selected.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/crushlevel_heart.svg b/public/icons/crushlevel_heart.svg new file mode 100644 index 0000000..da5b5a3 --- /dev/null +++ b/public/icons/crushlevel_heart.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/diamond.svg b/public/icons/diamond.svg new file mode 100644 index 0000000..d2f0b5a --- /dev/null +++ b/public/icons/diamond.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/empty.svg b/public/icons/empty.svg new file mode 100644 index 0000000..30a19c3 --- /dev/null +++ b/public/icons/empty.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/expand.svg b/public/icons/expand.svg new file mode 100644 index 0000000..23e1f0d --- /dev/null +++ b/public/icons/expand.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/explore.svg b/public/icons/explore.svg new file mode 100644 index 0000000..e3ff142 --- /dev/null +++ b/public/icons/explore.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/icons/explore_selected.svg b/public/icons/explore_selected.svg new file mode 100644 index 0000000..48d0f0c --- /dev/null +++ b/public/icons/explore_selected.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/female.svg b/public/icons/female.svg new file mode 100644 index 0000000..871a53f --- /dev/null +++ b/public/icons/female.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/icons/fold.svg b/public/icons/fold.svg new file mode 100644 index 0000000..47abc98 --- /dev/null +++ b/public/icons/fold.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/foryou.svg b/public/icons/foryou.svg new file mode 100644 index 0000000..e710aeb --- /dev/null +++ b/public/icons/foryou.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/icons/foryou_selected.svg b/public/icons/foryou_selected.svg new file mode 100644 index 0000000..47ca9fe --- /dev/null +++ b/public/icons/foryou_selected.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/gender-neutral.svg b/public/icons/gender-neutral.svg new file mode 100644 index 0000000..6d19b36 --- /dev/null +++ b/public/icons/gender-neutral.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/generate-logo.svg b/public/icons/generate-logo.svg new file mode 100644 index 0000000..71d22eb --- /dev/null +++ b/public/icons/generate-logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/icons/heart-single.svg b/public/icons/heart-single.svg new file mode 100644 index 0000000..e6f6b2a --- /dev/null +++ b/public/icons/heart-single.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/heart.svg b/public/icons/heart.svg new file mode 100644 index 0000000..ee9bb9d --- /dev/null +++ b/public/icons/heart.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/icon-crush.svg b/public/icons/icon-crush.svg new file mode 100644 index 0000000..582372f --- /dev/null +++ b/public/icons/icon-crush.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/image-generate-loading.png b/public/icons/image-generate-loading.png new file mode 100644 index 0000000..889ccb5 Binary files /dev/null and b/public/icons/image-generate-loading.png differ diff --git a/public/icons/image-generate-loading.svg b/public/icons/image-generate-loading.svg new file mode 100644 index 0000000..b0243d0 --- /dev/null +++ b/public/icons/image-generate-loading.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/public/icons/like-gradient.svg b/public/icons/like-gradient.svg new file mode 100644 index 0000000..25b6f58 --- /dev/null +++ b/public/icons/like-gradient.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/login-logo.svg b/public/icons/login-logo.svg new file mode 100644 index 0000000..57c0a3b --- /dev/null +++ b/public/icons/login-logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/male.svg b/public/icons/male.svg new file mode 100644 index 0000000..15dd2f1 --- /dev/null +++ b/public/icons/male.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/icons/notice.svg b/public/icons/notice.svg new file mode 100644 index 0000000..c9deecd --- /dev/null +++ b/public/icons/notice.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/question-border.svg b/public/icons/question-border.svg new file mode 100644 index 0000000..b596a85 --- /dev/null +++ b/public/icons/question-border.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/radio-checked.svg b/public/icons/radio-checked.svg new file mode 100644 index 0000000..8156bbc --- /dev/null +++ b/public/icons/radio-checked.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/square-logo.svg b/public/icons/square-logo.svg new file mode 100644 index 0000000..80118d9 --- /dev/null +++ b/public/icons/square-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icons/status-error.svg b/public/icons/status-error.svg new file mode 100644 index 0000000..2c155ac --- /dev/null +++ b/public/icons/status-error.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/status-successful.svg b/public/icons/status-successful.svg new file mode 100644 index 0000000..bf5b446 --- /dev/null +++ b/public/icons/status-successful.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/vip-black.svg b/public/icons/vip-black.svg new file mode 100644 index 0000000..2191c09 --- /dev/null +++ b/public/icons/vip-black.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/vip-square.svg b/public/icons/vip-square.svg new file mode 100644 index 0000000..042dcac --- /dev/null +++ b/public/icons/vip-square.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icons/vip.svg b/public/icons/vip.svg new file mode 100644 index 0000000..31b1c7c --- /dev/null +++ b/public/icons/vip.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/about/banner.png b/public/images/about/banner.png new file mode 100644 index 0000000..5d63e30 Binary files /dev/null and b/public/images/about/banner.png differ diff --git a/public/images/bubble-nine-patch.png b/public/images/bubble-nine-patch.png new file mode 100644 index 0000000..b679437 Binary files /dev/null and b/public/images/bubble-nine-patch.png differ diff --git a/public/images/chat-bubble/1.png b/public/images/chat-bubble/1.png new file mode 100644 index 0000000..aa135df Binary files /dev/null and b/public/images/chat-bubble/1.png differ diff --git a/public/images/chat-bubble/2.png b/public/images/chat-bubble/2.png new file mode 100644 index 0000000..e0735b8 Binary files /dev/null and b/public/images/chat-bubble/2.png differ diff --git a/public/images/chat-bubble/20250903-163830.png b/public/images/chat-bubble/20250903-163830.png new file mode 100644 index 0000000..ec9b6ac Binary files /dev/null and b/public/images/chat-bubble/20250903-163830.png differ diff --git a/public/images/chat-bubble/chat_bubble_1.png b/public/images/chat-bubble/chat_bubble_1.png new file mode 100644 index 0000000..aa135df Binary files /dev/null and b/public/images/chat-bubble/chat_bubble_1.png differ diff --git a/public/images/chat-bubble/飞书20250828-102316.mp4 b/public/images/chat-bubble/飞书20250828-102316.mp4 new file mode 100644 index 0000000..8553d38 Binary files /dev/null and b/public/images/chat-bubble/飞书20250828-102316.mp4 differ diff --git a/public/images/creator/icon-gift.png b/public/images/creator/icon-gift.png new file mode 100644 index 0000000..b154c61 Binary files /dev/null and b/public/images/creator/icon-gift.png differ diff --git a/public/images/creator/icon-image.png b/public/images/creator/icon-image.png new file mode 100644 index 0000000..d0a5903 Binary files /dev/null and b/public/images/creator/icon-image.png differ diff --git a/public/images/creator/icon-more.png b/public/images/creator/icon-more.png new file mode 100644 index 0000000..d1a707e Binary files /dev/null and b/public/images/creator/icon-more.png differ diff --git a/public/images/creator/icon-star.png b/public/images/creator/icon-star.png new file mode 100644 index 0000000..643d2ab Binary files /dev/null and b/public/images/creator/icon-star.png differ diff --git a/public/images/crushcoin/banner.png b/public/images/crushcoin/banner.png new file mode 100644 index 0000000..028d0e5 Binary files /dev/null and b/public/images/crushcoin/banner.png differ diff --git a/public/images/crushcoin/checked-diamond.svg b/public/images/crushcoin/checked-diamond.svg new file mode 100644 index 0000000..7559253 --- /dev/null +++ b/public/images/crushcoin/checked-diamond.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/crushcoin/divider.png b/public/images/crushcoin/divider.png new file mode 100644 index 0000000..59bc8ef Binary files /dev/null and b/public/images/crushcoin/divider.png differ diff --git a/public/images/crushcoin/icon-star.svg b/public/images/crushcoin/icon-star.svg new file mode 100644 index 0000000..7190cc5 --- /dev/null +++ b/public/images/crushcoin/icon-star.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/crushlevel/bg-bottom.png b/public/images/crushlevel/bg-bottom.png new file mode 100644 index 0000000..3218499 Binary files /dev/null and b/public/images/crushlevel/bg-bottom.png differ diff --git a/public/images/crushlevel/bg-top.png b/public/images/crushlevel/bg-top.png new file mode 100644 index 0000000..31a887a Binary files /dev/null and b/public/images/crushlevel/bg-top.png differ diff --git a/public/images/crushlevel/heart.png b/public/images/crushlevel/heart.png new file mode 100644 index 0000000..7402ad8 Binary files /dev/null and b/public/images/crushlevel/heart.png differ diff --git a/public/images/home/banner-create-icon.png b/public/images/home/banner-create-icon.png new file mode 100644 index 0000000..f8eecd8 Binary files /dev/null and b/public/images/home/banner-create-icon.png differ diff --git a/public/images/home/banner-create.png b/public/images/home/banner-create.png new file mode 100644 index 0000000..3b4b55c Binary files /dev/null and b/public/images/home/banner-create.png differ diff --git a/public/images/home/banner-o.png b/public/images/home/banner-o.png new file mode 100644 index 0000000..4e3a8ba Binary files /dev/null and b/public/images/home/banner-o.png differ diff --git a/public/images/home/banner.png b/public/images/home/banner.png new file mode 100644 index 0000000..b9be3ff Binary files /dev/null and b/public/images/home/banner.png differ diff --git a/public/images/home/banner_o.png b/public/images/home/banner_o.png new file mode 100644 index 0000000..4d98414 Binary files /dev/null and b/public/images/home/banner_o.png differ diff --git a/public/images/home/bg-1st.svg b/public/images/home/bg-1st.svg new file mode 100644 index 0000000..80a299d --- /dev/null +++ b/public/images/home/bg-1st.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/home/bg-2nd.svg b/public/images/home/bg-2nd.svg new file mode 100644 index 0000000..de70ba4 --- /dev/null +++ b/public/images/home/bg-2nd.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/home/bg-3rd.svg b/public/images/home/bg-3rd.svg new file mode 100644 index 0000000..ee9dac0 --- /dev/null +++ b/public/images/home/bg-3rd.svg @@ -0,0 +1,10 @@ + +
+ + + + + + + +
diff --git a/public/images/home/bg-star.png b/public/images/home/bg-star.png new file mode 100644 index 0000000..825d7bc Binary files /dev/null and b/public/images/home/bg-star.png differ diff --git a/public/images/home/icon-anime.png b/public/images/home/icon-anime.png new file mode 100644 index 0000000..659d92e Binary files /dev/null and b/public/images/home/icon-anime.png differ diff --git a/public/images/home/icon-arrow-bottom.svg b/public/images/home/icon-arrow-bottom.svg new file mode 100644 index 0000000..2cc12aa --- /dev/null +++ b/public/images/home/icon-arrow-bottom.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/home/icon-arrow-down-fill.svg b/public/images/home/icon-arrow-down-fill.svg new file mode 100644 index 0000000..2cc12aa --- /dev/null +++ b/public/images/home/icon-arrow-down-fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/home/icon-crush-free.png b/public/images/home/icon-crush-free.png new file mode 100644 index 0000000..5281028 Binary files /dev/null and b/public/images/home/icon-crush-free.png differ diff --git a/public/images/home/icon-film.png b/public/images/home/icon-film.png new file mode 100644 index 0000000..8d83f13 Binary files /dev/null and b/public/images/home/icon-film.png differ diff --git a/public/images/home/icon-game.png b/public/images/home/icon-game.png new file mode 100644 index 0000000..7d68af1 Binary files /dev/null and b/public/images/home/icon-game.png differ diff --git a/public/images/home/icon-original.png b/public/images/home/icon-original.png new file mode 100644 index 0000000..21a64ae Binary files /dev/null and b/public/images/home/icon-original.png differ diff --git a/public/images/home/left-star.png b/public/images/home/left-star.png new file mode 100644 index 0000000..692d835 Binary files /dev/null and b/public/images/home/left-star.png differ diff --git a/public/images/home/right-star.png b/public/images/home/right-star.png new file mode 100644 index 0000000..bfdfa75 Binary files /dev/null and b/public/images/home/right-star.png differ diff --git a/public/images/home/shining.svg b/public/images/home/shining.svg new file mode 100644 index 0000000..92c86e9 --- /dev/null +++ b/public/images/home/shining.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/leaderboard/1-st.svg b/public/images/leaderboard/1-st.svg new file mode 100644 index 0000000..8512b7b --- /dev/null +++ b/public/images/leaderboard/1-st.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/leaderboard/2-st.svg b/public/images/leaderboard/2-st.svg new file mode 100644 index 0000000..250e1e7 --- /dev/null +++ b/public/images/leaderboard/2-st.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/leaderboard/3-st.svg b/public/images/leaderboard/3-st.svg new file mode 100644 index 0000000..e4a52fb --- /dev/null +++ b/public/images/leaderboard/3-st.svg @@ -0,0 +1,10 @@ + +
+ + + + + + + +
diff --git a/public/images/leaderboard/bg.png b/public/images/leaderboard/bg.png new file mode 100644 index 0000000..4a4c4c0 Binary files /dev/null and b/public/images/leaderboard/bg.png differ diff --git a/public/images/login/asset-1.png b/public/images/login/asset-1.png new file mode 100644 index 0000000..04fcb88 Binary files /dev/null and b/public/images/login/asset-1.png differ diff --git a/public/images/login/asset-2.png b/public/images/login/asset-2.png new file mode 100644 index 0000000..b74c926 Binary files /dev/null and b/public/images/login/asset-2.png differ diff --git a/public/images/login/asset-3.png b/public/images/login/asset-3.png new file mode 100644 index 0000000..c6051fa Binary files /dev/null and b/public/images/login/asset-3.png differ diff --git a/public/images/login/asset-4.png b/public/images/login/asset-4.png new file mode 100644 index 0000000..b15f8d3 Binary files /dev/null and b/public/images/login/asset-4.png differ diff --git a/public/images/login/asset-5.png b/public/images/login/asset-5.png new file mode 100644 index 0000000..edf1ddd Binary files /dev/null and b/public/images/login/asset-5.png differ diff --git a/public/images/login/asset-6.png b/public/images/login/asset-6.png new file mode 100644 index 0000000..14feba4 Binary files /dev/null and b/public/images/login/asset-6.png differ diff --git a/public/images/login/asset-7.png b/public/images/login/asset-7.png new file mode 100644 index 0000000..7552f53 Binary files /dev/null and b/public/images/login/asset-7.png differ diff --git a/public/images/login/asset-8.png b/public/images/login/asset-8.png new file mode 100644 index 0000000..1549808 Binary files /dev/null and b/public/images/login/asset-8.png differ diff --git a/public/images/login/asset-9.png b/public/images/login/asset-9.png new file mode 100644 index 0000000..f6e9cbb Binary files /dev/null and b/public/images/login/asset-9.png differ diff --git a/public/images/login/logo.svg b/public/images/login/logo.svg new file mode 100644 index 0000000..40256b4 --- /dev/null +++ b/public/images/login/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/login/v1/1.png b/public/images/login/v1/1.png new file mode 100644 index 0000000..ecbbef5 Binary files /dev/null and b/public/images/login/v1/1.png differ diff --git a/public/images/login/v1/10.png b/public/images/login/v1/10.png new file mode 100644 index 0000000..e595a8d Binary files /dev/null and b/public/images/login/v1/10.png differ diff --git a/public/images/login/v1/2.png b/public/images/login/v1/2.png new file mode 100644 index 0000000..d404b5e Binary files /dev/null and b/public/images/login/v1/2.png differ diff --git a/public/images/login/v1/3.png b/public/images/login/v1/3.png new file mode 100644 index 0000000..6fd5b27 Binary files /dev/null and b/public/images/login/v1/3.png differ diff --git a/public/images/login/v1/4.png b/public/images/login/v1/4.png new file mode 100644 index 0000000..cb69558 Binary files /dev/null and b/public/images/login/v1/4.png differ diff --git a/public/images/login/v1/5.png b/public/images/login/v1/5.png new file mode 100644 index 0000000..82fb6ad Binary files /dev/null and b/public/images/login/v1/5.png differ diff --git a/public/images/login/v1/6.png b/public/images/login/v1/6.png new file mode 100644 index 0000000..d141dad Binary files /dev/null and b/public/images/login/v1/6.png differ diff --git a/public/images/login/v1/7.png b/public/images/login/v1/7.png new file mode 100644 index 0000000..7ee4c90 Binary files /dev/null and b/public/images/login/v1/7.png differ diff --git a/public/images/login/v1/8.png b/public/images/login/v1/8.png new file mode 100644 index 0000000..b03cc31 Binary files /dev/null and b/public/images/login/v1/8.png differ diff --git a/public/images/login/v1/9.png b/public/images/login/v1/9.png new file mode 100644 index 0000000..fcdee7c Binary files /dev/null and b/public/images/login/v1/9.png differ diff --git a/public/images/login/v1/bg.png b/public/images/login/v1/bg.png new file mode 100644 index 0000000..4bc4d5f Binary files /dev/null and b/public/images/login/v1/bg.png differ diff --git a/public/images/login/v1/icon-star-right.svg b/public/images/login/v1/icon-star-right.svg new file mode 100644 index 0000000..d546643 --- /dev/null +++ b/public/images/login/v1/icon-star-right.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/profile/creator.svg b/public/images/profile/creator.svg new file mode 100644 index 0000000..ea934a2 --- /dev/null +++ b/public/images/profile/creator.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/profile/membership.svg b/public/images/profile/membership.svg new file mode 100644 index 0000000..6a40d1d --- /dev/null +++ b/public/images/profile/membership.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/profile/wallet.svg b/public/images/profile/wallet.svg new file mode 100644 index 0000000..8c5436d --- /dev/null +++ b/public/images/profile/wallet.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/share/ep.png b/public/images/share/ep.png new file mode 100644 index 0000000..591ad26 Binary files /dev/null and b/public/images/share/ep.png differ diff --git a/public/images/share/logo.svg b/public/images/share/logo.svg new file mode 100644 index 0000000..6b3b730 --- /dev/null +++ b/public/images/share/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/vip/drawer-bg.png b/public/images/vip/drawer-bg.png new file mode 100644 index 0000000..51d510a Binary files /dev/null and b/public/images/vip/drawer-bg.png differ diff --git a/public/images/vip/icon-vip.svg b/public/images/vip/icon-vip.svg new file mode 100644 index 0000000..0f9ad65 --- /dev/null +++ b/public/images/vip/icon-vip.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/wallet/dashboard-bg.svg b/public/images/wallet/dashboard-bg.svg new file mode 100644 index 0000000..64d8b9d --- /dev/null +++ b/public/images/wallet/dashboard-bg.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/wallet/icon-error.png b/public/images/wallet/icon-error.png new file mode 100644 index 0000000..868e833 Binary files /dev/null and b/public/images/wallet/icon-error.png differ diff --git a/public/images/wallet/icon-image.png b/public/images/wallet/icon-image.png new file mode 100644 index 0000000..51fdd51 Binary files /dev/null and b/public/images/wallet/icon-image.png differ diff --git a/public/images/wallet/icon-pending.png b/public/images/wallet/icon-pending.png new file mode 100644 index 0000000..8b843bb Binary files /dev/null and b/public/images/wallet/icon-pending.png differ diff --git a/public/images/wallet/icon-success.png b/public/images/wallet/icon-success.png new file mode 100644 index 0000000..ea26807 Binary files /dev/null and b/public/images/wallet/icon-success.png differ diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json new file mode 100644 index 0000000..7bbc6e4 --- /dev/null +++ b/public/locales/en/translation.json @@ -0,0 +1,851 @@ +{ + "_.notfound.title.empty_title": "Oops, there’s nothing here…", + "common.mockprovider.text.div": "Initializing development environment...", + "common.mockprovider.text.p": "Initializing development environment...", + "common.uselogout.toast.toast_success": "Logged out", + "common.useregister.toast.toast_success": "Successful registration!", + "common.useregister.toast.toast_error": "Registration failed.", + "debug_mock.debugmockpage.text.div": "Waiting for the test results...", + "debug_mock.debugmockpage.text.h1": "🔧 Mock API 调试页面", + "debug_mock.debugmockpage.text.h2": "Test Results", + "debug_mock.debugmockpage.text.button": "Clear result", + "debug_mock.debugmockpage.text.strong": "Service Worker支持:", + "demo.demopage.text.div": "Toggle Loading 4 Toggle Loading 4 Toggle Loading 4", + "demo.demopage.text.h1": "Component demo", + "demo.demopage.text.section": "Toggle Loading 4 Toggle Loading 4 Toggle Loading 4", + "demo.demopage.text.h2": "Manually Control Loading Status Manually Control Loading Status Manually Control Loading Status", + "demo.demopage.text.p": "The left sidebar supports the following functions:", + "demo.demopage.text.ul": "Smooth animation transitions", + "demo.demopage.text.li": "Smooth animation transitions", + "demo.demopage.text.alertdialog": "Continue operation", + "demo.demopage.text.alertdialogtrigger": "No close button", + "demo.demopage.text.button": "Toggle Loading 4 Toggle Loading 4 Toggle Loading 4", + "demo.demopage.text.alertdialogcontent": "Continue operation", + "demo.demopage.text.alertdialogheader": "This is an important reminder that you must choose an option to proceed.", + "demo.demopage.text.alertdialogtitle": "Important Note", + "demo.demopage.text.alertdialogdescription": "This is an important reminder that you must choose an option to proceed.", + "demo.demopage.text.alertdialogfooter": "Continue operation", + "demo.demopage.text.alertdialogcancel": "Oh, I see.", + "demo.demopage.text.alertdialogaction": "Continue operation", + "demo.demopage.text.iconbutton": "🗑️", + "demo.demopage.text.span": "🗑️", + "server_device_test.serverdevicetestpage.text.div": "This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 +", + "server_device_test.serverdevicetestpage.text.h1": "🖥️ 服务端设备ID测试", + "server_device_test.serverdevicetestpage.text.p": "This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 +", + "server_device_test.serverdevicetestpage.text.card": "This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 +", + "server_device_test.serverdevicetestpage.text.cardheader": "📋 服务端设备ID处理流程", + "server_device_test.serverdevicetestpage.text.cardtitle": "📋 服务端设备ID处理流程", + "server_device_test.serverdevicetestpage.text.cardcontent": "This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 + This is an architectural limitation of Next.js 13 +", + "server_device_test.serverdevicetestpage.text.h4": "rendering environment", + "server_device_test.serverdevicetestpage.text.ul": "• Device ID is passed to server level component via header • Device ID is passed to server level component via header", + "server_device_test.serverdevicetestpage.text.li": "• Device ID is passed to server level component via header • Device ID is passed to server level component via header", + "server_device_test.serverdevicetestpage.text.h5": "4. Restrictions 4. Restrictions", + "test_avatar_crop.testavatarcroppage.text.div": "• Smooth interactive animation effects • Smooth interactive animation effects", + "test_avatar_crop.testavatarcroppage.text.h1": "Avatar Crop Component Test", + "test_avatar_crop.testavatarcroppage.text.p": "Avatar clipping pop-up window based on design draft restoration", + "test_avatar_crop.testavatarcroppage.text.card": "• Smooth interactive animation effects • Smooth interactive animation effects", + "test_avatar_crop.testavatarcroppage.text.h2": "Design Restore", + "test_avatar_crop.testavatarcroppage.text.button": "Download avatar", + "test_avatar_crop.testavatarcroppage.text.span": "Selected:", + "test_avatar_crop.testavatarcroppage.alt.image_alt": "Cropped avatar", + "test_avatar_crop.testavatarcroppage.text.h3": "🎨 设计细节", + "test_avatar_setting.testavatarsettingpage.text.div": "Open avatar settings", + "test_avatar_setting.testavatarsettingpage.text.h1": "avatar setup test", + "test_avatar_setting.testavatarsettingpage.alt.img_alt": "Current Avatar", + "test_avatar_setting.testavatarsettingpage.text.button": "Open avatar settings", + "test_discord.testdiscordpage.toast.toast_error": "Mock login failed", + "test_discord.testdiscordpage.toast.toast_warning": "Missing configuration", + "test_discord.testdiscordpage.toast.toast_success": "Exit successfully", + "test_discord.testdiscordpage.text.div": "POST /web/third/login { \"appClient\": \"WEB\", \"deviceCode\": \"设备ID\", \"thirdToken\": \"discord_code\", \"thirdType\": \"DISCORD\" }", + "test_discord.testdiscordpage.text.h1": "Discord login test page", + "test_discord.testdiscordpage.text.p": "Http://localhost:3000/api/auth/discord/callback", + "test_discord.testdiscordpage.text.card": "POST /web/third/login { \"appClient\": \"WEB\", \"deviceCode\": \"设备ID\", \"thirdToken\": \"discord_code\", \"thirdType\": \"DISCORD\" }", + "test_discord.testdiscordpage.text.cardheader": "configuration instructions", + "test_discord.testdiscordpage.text.cardtitle": "configuration instructions", + "test_discord.testdiscordpage.text.cardcontent": "POST /web/third/login { \"appClient\": \"WEB\", \"deviceCode\": \"设备ID\", \"thirdToken\": \"discord_code\", \"thirdType\": \"DISCORD\" }", + "test_discord.testdiscordpage.text.strong": "Need to improve information:", + "test_discord.testdiscordpage.text.button": "clear log", + "test_discord.testdiscordpage.text.h4": "API interface:", + "test_discord.testdiscordpage.text.ol": "The front-end login page detects the code, and calls the back-end API to complete the login.", + "test_discord.testdiscordpage.text.li": "The front-end login page detects the code, and calls the back-end API to complete the login.", + "test_discord.testdiscordpage.text.pre": "POST /web/third/login { \"appClient\": \"WEB\", \"deviceCode\": \"设备ID\", \"thirdToken\": \"discord_code\", \"thirdType\": \"DISCORD\" }", + "test_image_crop.testimagecroppage.text.div": "Original image preview", + "test_image_crop.testimagecroppage.text.h1": "Image cropping test", + "test_image_crop.testimagecroppage.text.p": "Test various image cropping features and preset configurations", + "test_image_crop.testimagecroppage.text.card": "Original image preview", + "test_image_crop.testimagecroppage.text.h2": "Original image preview", + "test_image_crop.testimagecroppage.text.button": "download", + "test_image_crop.testimagecroppage.text.span": "Selected:", + "test_image_crop.testimagecroppage.alt.image_alt": "original image", + "test_image_crop.testimagecroppage.title.imagecropmodal_title": "Advanced image crop", + "test_image_crop.testimagecroppage.title.simpleimagecropmodal_title": "Simple image cropping", + "test_lamejs.testlamejs.text.div": "这个页面用于测试 lamejs 库是否能正确导入和初始化。 请打开浏览器控制台查看详细的日志信息。", + "test_lamejs.testlamejs.text.h1": "lamejs 导入测试", + "test_lamejs.testlamejs.text.button": "测试 lamejs 导入", + "test_lamejs.testlamejs.text.h3": "说明", + "test_lamejs.testlamejs.text.p": "这个页面用于测试 lamejs 库是否能正确导入和初始化。 请打开浏览器控制台查看详细的日志信息。", + "test_middleware.testmiddlewarepage.text.div": "If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing.", + "test_middleware.testmiddlewarepage.text.h1": "🔧 Middleware 测试页面", + "test_middleware.testmiddlewarepage.text.h2": "Navigation test", + "test_middleware.testmiddlewarepage.text.button": "Programmatically navigate to /profile Programmatically navigate to /profile", + "test_middleware.testmiddlewarepage.text.h3": "explain", + "test_middleware.testmiddlewarepage.text.p": "If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing. If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing.", + "test_mp3_conversion.testmp3conversion.toast.toast_success": "原始文件已下载", + "test_mp3_conversion.testmp3conversion.toast.toast_error": "录音失败", + "test_mp3_conversion.testmp3conversion.text.div": "您的浏览器不支持音频播放。", + "test_mp3_conversion.testmp3conversion.text.h1": "MP3转换测试", + "test_mp3_conversion.testmp3conversion.text.button": "下载MP3文件", + "test_mp3_conversion.testmp3conversion.text.p": "压缩率:", + "test_mp3_conversion.testmp3conversion.text.h3": "音频播放测试", + "test_mp3_conversion.testmp3conversion.text.audio": "您的浏览器不支持音频播放。", + "test_s3_upload.tests3uploadpage.text.div": "import { useS3Upload } from '@/hooks/useS3Upload' import { BizTypeEnum } from '@/services/common/types' const { uploading, progress, error, uploadFile } = useS3Upload({ bizType: BizTypeEnum.Role, maxRetries: 3, retryDelay: 2000, onSuccess: (url) => console.log('上传成功:', url), onError: (error) => console.error('上传失败:', error), onProgress: (progress) => console.log('进度:', progress.percentage + '%') }) // 使用 await uploadFile(file)", + "test_s3_upload.tests3uploadpage.text.h1": "AWS S3 Upload Test AWS S3 Upload Test AWS S3 Upload Test", + "test_s3_upload.tests3uploadpage.text.p": "Testing the file upload feature using the AWS S3 SDK Testing the file upload feature using the AWS S3 SDK Testing the file upload feature using the AWS S3 SDK Testing the file upload feature using the AWS S3 SDK Testing the file upload feature using the AWS S3 SDK", + "test_s3_upload.tests3uploadpage.text.h2": "How to use Hook How to use Hook", + "test_s3_upload.tests3uploadpage.text.pre": "import { useS3Upload } from '@/hooks/useS3Upload' import { BizTypeEnum } from '@/services/common/types' const { uploading, progress, error, uploadFile } = useS3Upload({ bizType: BizTypeEnum.Role, maxRetries: 3, retryDelay: 2000, onSuccess: (url) => console.log('上传成功:', url), onError: (error) => console.error('上传失败:', error), onProgress: (progress) => console.log('进度:', progress.percentage + '%') }) // 使用 await uploadFile(file)", + "common.airelationtag.text.div": "·", + "common.airelationtag.text.span": ">", + "common.airelationtag.text.tag": ">", + "common.s3uploaddemo.text.div": "✅ Presigned URL secure upload ✅ Presigned URL secure upload ✅ Presigned URL secure upload ✅ Presigned URL secure upload", + "common.s3uploaddemo.text.h3": "S3 file upload demo S3 file upload demo", + "common.s3uploaddemo.text.p": "MB)", + "common.s3uploaddemo.text.button": "reset", + "common.s3uploaddemo.text.span": "MB", + "common.s3uploaddemo.text.h4": "Functional features:", + "common.s3uploaddemo.text.ul": "✅ Presigned URL secure upload ✅ Presigned URL secure upload ✅ Presigned URL secure upload ✅ Presigned URL secure upload", + "common.s3uploaddemo.text.li": "✅ Presigned URL secure upload ✅ Presigned URL secure upload ✅ Presigned URL secure upload ✅ Presigned URL secure upload", + "common.abandoncreationdialog.text.alertdialog": "cancel", + "common.abandoncreationdialog.text.alertdialogcontent": "cancel", + "common.abandoncreationdialog.text.alertdialogheader": "Give up creation", + "common.abandoncreationdialog.text.alertdialogtitle": "Give up creation", + "common.abandoncreationdialog.text.alertdialogdescription": "If you choose to exit or regenerate the image, the already created image will disappear, and the number of creations will be consumed once.", + "common.abandoncreationdialog.text.alertdialogfooter": "cancel", + "common.abandoncreationdialog.text.alertdialogcancel": "cancel", + "common.handleconfirm.toast.toast_error": "Please enter nickname", + "common.aigeneratebutton.text.div": "AI Generate", + "common.aigeneratebutton.text.span": "AI Generate", + "common.aigeneratebutton.text.alertdialog": "Continue", + "common.aigeneratebutton.text.alertdialogcontent": "Continue", + "common.aigeneratebutton.text.alertdialogheader": "Warning", + "common.aigeneratebutton.text.alertdialogtitle": "Warning", + "common.aigeneratebutton.text.alertdialogdescription": "Using AI generation will overwrite the content you have already entered. Do you want to continue?", + "common.aigeneratebutton.text.alertdialogfooter": "Continue", + "common.aigeneratebutton.text.alertdialogcancel": "Cancel", + "common.aigeneratebutton.text.alertdialogaction": "Continue", + "common.albumdeletealert.text.alertdialog": "Delete", + "common.albumdeletealert.text.alertdialogcontent": "Delete", + "common.albumdeletealert.text.alertdialogheader": "Delete Album", + "common.albumdeletealert.text.alertdialogtitle": "Delete Album", + "common.albumdeletealert.text.alertdialogfooter": "Delete", + "common.albumdeletealert.text.alertdialogaction": "Got it", + "common.albumdeletealert.text.button": "Delete", + "common.schema.validation.message": "At least 20 diamonds", + "common.albumpricesetting.text.alertdialog": "Confirm", + "common.albumpricesetting.text.alertdialogcontent": "Confirm", + "common.albumpricesetting.text.alertdialogheader": "Unlock Method", + "common.albumpricesetting.text.alertdialogtitle": "Unlock Method", + "common.albumpricesetting.text.form": "Confirm", + "common.albumpricesetting.text.alertdialogdescription": "Price", + "common.albumpricesetting.text.div": "Price", + "common.albumpricesetting.text.formitem": "Price", + "common.albumpricesetting.text.formlabel": "Price", + "common.albumpricesetting.text.tooltip": "Set up several free images to attract interlocutors to interact with your avatar", + "common.albumpricesetting.text.tooltipcontent": "Set up several free images to attract interlocutors to interact with your avatar", + "common.albumpricesetting.text.p": "Set up several free images to attract interlocutors to interact with your avatar", + "common.albumpricesetting.text.formcontrol": "Free", + "common.albumpricesetting.text.select": "Free", + "common.albumpricesetting.text.selectcontent": "Free", + "common.albumpricesetting.text.selectitem": "Free", + "common.albumpricesetting.placeholder.input_placeholder": "Price", + "common.albumpricesetting.alt.image_alt": "Diamond", + "common.albumpricesetting.text.alertdialogfooter": "Confirm", + "common.albumpricesetting.text.button": "Confirm", + "common.handlepayment.toast.toast_error": "Payment failure, please try again", + "common.renderlist.text.div": "$", + "common.renderlist.alt.image_alt": "diamond", + "common.chargedrawer.text.drawer": "Cancel", + "common.chargedrawer.text.drawercontent": "Cancel", + "common.chargedrawer.text.drawerheader": "Charge", + "common.chargedrawer.text.drawertitle": "Charge", + "common.chargedrawer.text.drawerdescription": "The Crush coin is insufficient and cannot continue, please recharge", + "common.chargedrawer.text.div": "Cancel", + "common.chargedrawer.text.span": "Agreement", + "common.chargedrawer.text.link": "Agreement", + "common.chargedrawer.text.drawerfooter": "Cancel", + "common.questionicon.text.tooltip": "Voice call message price refers to the price of a voice call conversation with the character, calculated by item", + "common.questionicon.text.tooltipcontent": "Voice call message price refers to the price of a voice call conversation with the character, calculated by item", + "common.questionicon.text.div": "Voice call message price refers to the price of a voice call conversation with the character, calculated by item", + "common.questionicon.text.p": "Voice call message price refers to the price of a voice call conversation with the character, calculated by item", + "common.coininsufficientdialog.text.alertdialog": "Recharge", + "common.coininsufficientdialog.text.alertdialogcontent": "Recharge", + "common.coininsufficientdialog.text.alertdialogheader": "Crush Coin insufficient", + "common.coininsufficientdialog.text.alertdialogtitle": "Crush Coin insufficient", + "common.coininsufficientdialog.text.p": "Have a role-playing conversation with AI", + "common.coininsufficientdialog.text.div": "Recharge", + "common.coininsufficientdialog.text.h3": "Role-playing model", + "common.coininsufficientdialog.text.span": "Your Balance", + "common.coininsufficientdialog.text.button": "Recharge", + "common.createreachedlimitdialog.text.alertdialog": "you have reached the maximum number of AI creations.", + "common.createreachedlimitdialog.text.alertdialogcontent": "you have reached the maximum number of AI creations.", + "common.createreachedlimitdialog.text.alertdialogheader": "you have reached the maximum number of AI creations.", + "common.createreachedlimitdialog.text.alertdialogdescription": "you have reached the maximum number of AI creations.", + "common.deviceinfo.text.card": "• Will not be cleared when logging out (only clearAll will be cleared) • Will not be cleared when logging out (only clearAll will be cleared)", + "common.deviceinfo.text.cardheader": "📱 设备信息", + "common.deviceinfo.text.cardtitle": "📱 设备信息", + "common.deviceinfo.text.cardcontent": "• Will not be cleared when logging out (only clearAll will be cleared) • Will not be cleared when logging out (only clearAll will be cleared)", + "common.deviceinfo.text.div": "• Will not be cleared when logging out (only clearAll will be cleared) • Will not be cleared when logging out (only clearAll will be cleared)", + "common.deviceinfo.text.h4": "Authentication token (st) Authentication token (st)", + "common.deviceinfo.text.p": "Stored in a cookie, field named'st ', sent as AUTH_TK in the request header", + "common.deviceinfo.text.button": "Clear all data", + "common.deviceinfo.text.h5": "💡 设备ID说明", + "common.deviceinfo.text.ul": "• Will not be cleared when logging out (only clearAll will be cleared) • Will not be cleared when logging out (only clearAll will be cleared)", + "common.deviceinfo.text.li": "• Will not be cleared when logging out (only clearAll will be cleared) • Will not be cleared when logging out (only clearAll will be cleared)", + "common.items.aria.i_aria_hidden": "true", + "common.genderinput.aria.div_aria_label": "Gender", + "common.sidebar.alt.image_alt": "Expand", + "common.topbarwithoutlogin.alt.image_alt": "Logo", + "common.topbar.text.header": "Login in or Sign up", + "common.topbar.text.div": "Login in or Sign up", + "common.topbar.alt.image_alt": "logo", + "common.topbar.text.link": "Login in or Sign up", + "common.topbar.text.button": "Login in or Sign up", + "common.avatarcropmodal.text.div": "Confirm", + "common.empty.alt.image_alt": "Empty", + "common.imagecrop.text.div": "Rotate:", + "common.imagecrop.text.label": "Rotate:", + "common.imageviewer.text.imageviewerpaginationcontent": "/", + "common.imageviewer.text.div": "Choose", + "common.imageviewer.text.button": "Choose", + "common.defaultloadingmore.text.span": "Loading...", + "common.input.text.div": "/", + "common.input.text.span": "/", + "common.selectitem.alt.image_alt": "Check", + "_auth__about.aboutpage.text.div": "and the risky emotional gambles you feared to take.", + "_auth__about.aboutpage.alt.img_alt": "Banner", + "_auth__login.loginpage.text.div": "From \"Hello\" to \"I do\", every conversation is full of heart", + "_auth__login.loginpage.alt.image_alt": "Anime character", + "_main__chat.chatpage.title.empty_title": "Oops, there’s nothing here…", + "_main__contact.contactcard.text.div": "°", + "_main__contact.contactcard.alt.img_alt": "Heart", + "_main__contact.contactcard.text.span": "°", + "_main__contact.emptystate.text.div": "Start chatting with AI characters to build your contacts", + "_main__contact.emptystate.alt.img_alt": "Empty", + "_main__contact.emptystate.text.h3": "No contacts found", + "_main__contact.emptystate.text.p": "Start chatting with AI characters to build your contacts", + "_main__contact.contactspage.text.div": "Contacts", + "_main__contact.contactspage.text.h1": "Contacts", + "_main__creator.creatorpage.text.div": "Stay tuned", + "_main__creator.creatorpage.text.h1": "Creator", + "_main__creator.creatorpage.alt.image_alt": "gift", + "_main__crushcoin.crushcoinpage.text.div": "If you miss a check-in, the check-in count will reset and start again from day one.", + "_main__crushcoin.crushcoinpage.text.h1": "Daily free Crush Coin", + "_main__crushcoin.crushcoinpage.text.p": "If you miss a check-in, the check-in count will reset and start again from day one.", + "_main__explore.explorepage.text.div": "The third phase function is being polished, * & *... *...%...%... &% &...% &%... &% &... The third phase function is being polished, * & *... *...%...%... &% &...% &%... &% &...", + "_main__explore.explorepage.alt.image_alt": "Logo", + "_main__explore.explorepage.text.p": "The third phase function is being polished, * & *... *...%...%... &% &...% &%... &% &... The third phase function is being polished, * & *... *...%...%... &% &...% &%... &% &...", + "_main__leaderboard.leaderboardpage.text.div": "The gift list is ranked by the sum of the gift value received by the AI character.", + "_main__leaderboard.leaderboardpage.alt.image_alt": "Bg", + "_main__leaderboard.leaderboardpage.text.h1": "Leaderboard", + "_main__leaderboard.leaderboardpage.text.tooltip": "The gift list is ranked by the sum of the gift value received by the AI character.", + "_main__leaderboard.leaderboardpage.text.tooltipcontent": "The gift list is ranked by the sum of the gift value received by the AI character.", + "_main__leaderboard.leaderboardpage.text.p": "The gift list is ranked by the sum of the gift value received by the AI character.", + "_main__profile.profilepage.text.div": "ID:", + "_main__profile.profilepage.alt.image_alt": "Gender", + "_main__profile.profilepage.text.span": "ID:", + "_main__profile.profilepage.toast.toast_success": "Copied to clipboard", + "_main__test_voice_wave.testvoicewavepage.text.div": "import { VoiceWaveAnimation } from \"@/components/ui/voice-wave-animation\"; // 基础使用 // 自定义声波条数量 // 停止动画时的效果 ", + "_main__test_voice_wave.testvoicewavepage.text.h1": "Voice ripple animation effect display", + "_main__test_voice_wave.testvoicewavepage.text.h2": "How to use", + "_main__test_voice_wave.testvoicewavepage.text.p": "Features:", + "_main__test_voice_wave.testvoicewavepage.text.strong": "Features:", + "_main__test_voice_wave.testvoicewavepage.text.h3": "Article)", + "_main__test_voice_wave.testvoicewavepage.text.pre": "import { VoiceWaveAnimation } from \"@/components/ui/voice-wave-animation\"; // 基础使用 // 自定义声波条数量 // 停止动画时的效果 ", + "_main__test_voice_wave.testvoicewavepage.text.code": "import { VoiceWaveAnimation } from \"@/components/ui/voice-wave-animation\"; // 基础使用 // 自定义声波条数量 // 停止动画时的效果 ", + "_main__test_voice_wave.testvoicewavepage.text.ul": "Pure CSS implementation, excellent performance", + "_main__test_voice_wave.testvoicewavepage.text.li": "Pure CSS implementation, excellent performance", + "_main__vip.vippage.text.div": "Subscribe Now", + "_main__vip.vippage.text.h1": "Crush Level VIP", + "_main__vip.vippage.text.h2": "Crush Level VIP", + "_main__vip.vippage.text.button": "Subscribe Now", + "_main__wallet.walletpage.text.div": "Transaction Details >", + "_main__wallet.walletpage.text.h1": "Wallet", + "_main__wallet.walletpage.text.tabs": "Transaction Details >", + "_main__wallet.walletpage.text.tabslist": "Income", + "_main__wallet.walletpage.text.tabstrigger": "Income", + "_main__wallet.walletpage.text.link": "Transaction Details >", + "common.chatconversationsdeletedialog.text.alertdialog": "After deleting all messages, all message records will be cleared", + "common.chatconversationsdeletedialog.text.alertdialogcontent": "After deleting all messages, all message records will be cleared", + "common.chatconversationsdeletedialog.text.alertdialogheader": "Delete message", + "common.chatconversationsdeletedialog.text.alertdialogtitle": "Delete message", + "common.chatconversationsdeletedialog.text.alertdialogdescription": "After deleting all messages, all message records will be cleared", + "common.chatconversationsdeletedialog.text.alertdialogaction": "Delete", + "common.personitem.alt.image_alt": "Heart", + "common.personemptystate.title.empty_title": "We find nothing here", + "common.chatsearchresults.text.div": "Person", + "common.chatsearchresults.text.tabs": "Person", + "common.chatsearchresults.text.tabslist": "Person", + "common.chatsearchresults.text.tabstrigger": "Person", + "common.chatsearchresults.text.span": "Person", + "common.chatsidebar.text.div": "Chats", + "common.chatsidebar.text.span": "Chats", + "common.chatsidebar.placeholder.input_placeholder": "Search", + "common.chatsidebaraction.text.dropdownmenu": "Iconfont icon-Search", + "common.chatsidebaraction.text.dropdownmenucontent": "Iconfont icon-Search", + "common.chatsidebaraction.text.dropdownmenuitem": "Delete All", + "common.chatsidebaraction.text.span": "Delete All", + "common.chatsidebaritem.alt.image_alt": "heart", + "common.notice.text.div": "Notice", + "common.notice.alt.image_alt": "Notice", + "common.notice.text.span": "Notice", + "common.rendercheckbutton.text.button": "Check", + "common.noticedrawer.text.drawer": "Notice", + "common.noticedrawer.text.drawercontent": "Notice", + "common.noticedrawer.text.drawerheader": "Notice", + "_auth__login.discordbutton.toast.toast_error": "Discord登录失败", + "_auth__login.discordbutton.toast.toast_success": "Login successful", + "_auth__login.discordbutton.text.socialbutton": "Continue with Discord", + "_auth__login.imagecarousel.text.div": "No picture.", + "_auth__login.loginform.toast.toast_info": "Apple Sign In", + "_auth__login.loginform.text.div": "Privacy Policy", + "_auth__login.loginform.text.h2": "Log in/Sign up", + "_auth__login.loginform.text.p": "Privacy Policy", + "_auth__login.loginform.text.socialbutton": "Continue with Apple", + "_auth__login.loginform.text.link": "Privacy Policy", + "_auth__login_fields.schema.validation.message": "Please select your gender", + "_auth__login_fields.onsubmit.error.form_seterror": "Nickname already exists", + "_auth__login_fields.onsubmit.validation.message": "Nickname already exists", + "_auth__login_fields.fieldspage.text.div": "Submit", + "_auth__login_fields.fieldspage.alt.image_alt": "Anime character", + "_auth__login_fields.fieldspage.text.h2": "🎉🎉🎉 Welcome Join Us! 🎉🎉🎉", + "_auth__login_fields.fieldspage.text.form": "Submit", + "_auth__login_fields.fieldspage.text.formitem": "Can not edit after setting up", + "_auth__login_fields.fieldspage.text.formlabel": "Gender", + "_auth__login_fields.fieldspage.placeholder.input_placeholder": "Enter your nickname", + "_auth__login_fields.fieldspage.text.label": "Birthday", + "_auth__login_fields.fieldspage.placeholder.selectvalue_placeholder": "Day", + "_auth__login_fields.fieldspage.text.button": "Submit", + "_auth__policy_privacy.privacypolicypage.text.div": "Thank you for trusting Crushlevel. We strive to protect your information security!", + "_auth__policy_privacy.privacypolicypage.text.p": "Thank you for trusting Crushlevel. We strive to protect your information security!", + "_auth__policy_privacy.privacypolicypage.text.strong": "Account Deactivation:", + "_auth__policy_privacy.privacypolicypage.text.ul": "Troubleshoot issues, maintain service integrity, and protect your rights.", + "_auth__policy_privacy.privacypolicypage.text.li": "Troubleshoot issues, maintain service integrity, and protect your rights.", + "_auth__policy_recharge.rechargeagreementpage.text.div": "If you violate any provisions of this Agreement (including but not limited to purchasing Virtual Currency through unauthorized channels, using funds from illegal sources, etc.), the Platform has the right to take appropriate measures in accordance with the severity of the violation, including but not limited to warning, restricting account functions, temporarily or permanently banning your account, and requiring you to bear corresponding legal liability. If your violation causes losses to the Platform or third parties, you shall be liable for full compensation.", + "_auth__policy_recharge.rechargeagreementpage.text.p": "If you violate any provisions of this Agreement (including but not limited to purchasing Virtual Currency through unauthorized channels, using funds from illegal sources, etc.), the Platform has the right to take appropriate measures in accordance with the severity of the violation, including but not limited to warning, restricting account functions, temporarily or permanently banning your account, and requiring you to bear corresponding legal liability. If your violation causes losses to the Platform or third parties, you shall be liable for full compensation.", + "_auth__policy_recharge.rechargeagreementpage.text.strong": "the Platform does not provide refund services for this part of the Virtual Currency", + "_auth__policy_recharge.rechargeagreementpage.text.ul": "Other circumstances where you have intent or gross negligence (such as failure to update account security settings in a timely manner, ignoring account abnormal login reminders, etc.).", + "_auth__policy_recharge.rechargeagreementpage.text.li": "Other circumstances where you have intent or gross negligence (such as failure to update account security settings in a timely manner, ignoring account abnormal login reminders, etc.).", + "_auth__policy_tos.termsofservicepage.text.div": "Users are required to carefully read and strictly comply with the above agreement. Thank you for your support and trust in Crushlevel. We hope you enjoy using it!", + "_auth__policy_tos.termsofservicepage.text.p": "Users are required to carefully read and strictly comply with the above agreement. Thank you for your support and trust in Crushlevel. We hope you enjoy using it!", + "_auth__policy_tos.termsofservicepage.text.ul": "Other content that violates public order, good morals, or the provisions of this Agreement.", + "_auth__policy_tos.termsofservicepage.text.li": "Other content that violates public order, good morals, or the provisions of this Agreement.", + "_auth__share__userid_.notfound.alt.image_alt": "Anime character", + "_auth__share__userid_.notfound.title.empty_title": "Oops, there’s nothing here…", + "_auth__share__userid_.sharepage.text.div": "Chat. Crush. AI Date", + "_auth__share__userid_.sharepage.alt.image_alt": "Crushlevel", + "_auth__share__userid_.sharepage.text.button": "Chat", + "_auth__share__userid_.sharepage.text.p": "Intro:", + "_auth__share__userid_.sharepage.text.span": "Are you?", + "_main__chat__aiid_.notfound.title.empty_title": "Oops, there’s nothing here…", + "_main__contact.rendercontactstatustext.text.div": "Rank according to the total heart value of these characters", + "_main__contact.rendercontactstatustext.alt.image_alt": "Question-border", + "_main__contact.rendercontactstatustext.text.tooltip": "Rank according to the total heart value of these characters", + "_main__contact.rendercontactstatustext.text.tooltipcontent": "Rank according to the total heart value of these characters", + "_main__contact.rendercontactstatustext.text.p": "Rank according to the total heart value of these characters", + "_main__create.characterformschema.validation.message": "Please select gender", + "_main__create.onsubmit.error.form_seterror": "Nickname already exists", + "_main__create.onsubmit.validation.message": "Nickname already exists", + "_main__create.characterform.text.div": "Next", + "_main__create.characterform.text.h1": "Create Character", + "_main__create.characterform.text.form": "Next", + "_main__create.characterform.text.h2": "Character", + "_main__create.characterform.text.formitem": "Preferences", + "_main__create.characterform.text.formlabel": "Preferences", + "_main__create.characterform.placeholder.input_placeholder": "Enter character name", + "_main__create.characterform.text.label": "Birthday", + "_main__create.characterform.placeholder.selectvalue_placeholder": "Day", + "_main__create.characterform.placeholder.textarea_placeholder": "Describe the character's background, personality, and identity", + "_main__create.characterform.text.button": "Next", + "_main__create.closeiconbutton.text.alertdialog": "Quit", + "_main__create.closeiconbutton.text.alertdialogcontent": "Quit", + "_main__create.closeiconbutton.text.alertdialogheader": "The content has not been saved, Continue to quit?", + "_main__create.closeiconbutton.text.alertdialogtitle": "Unsaved changes", + "_main__create.closeiconbutton.text.alertdialogdescription": "The content has not been saved, Continue to quit?", + "_main__create.closeiconbutton.text.alertdialogfooter": "Quit", + "_main__create.closeiconbutton.text.alertdialogcancel": "Cancel", + "_main__create.closeiconbutton.text.alertdialogaction": "Quit", + "_main__create.copyrightrulemodal.text.alertdialog": "Confirm", + "_main__create.copyrightrulemodal.text.alertdialogcontent": "Confirm", + "_main__create.copyrightrulemodal.alt.image_alt": "Copyright Rule", + "_main__create.copyrightrulemodal.text.alertdialogdescription": "By clicking \"Confirm\", you represent and warrant that you are the original creator of this virtual character and that it does not infringe upon any third party's intellectual property rights or other legal rights.", + "_main__create.copyrightrulemodal.text.p": "By clicking \"Confirm\", you represent and warrant that you are the original creator of this virtual character and that it does not infringe upon any third party's intellectual property rights or other legal rights.", + "_main__create.copyrightrulemodal.text.alertdialogfooter": "Confirm", + "_main__create.copyrightrulemodal.text.alertdialogaction": "Confirm", + "_main__create.dialogueformschema.validation.message": "Please select voice", + "_main__create.dialogueform.text.div": "Female", + "_main__create.dialogueform.text.form": "Female", + "_main__create.dialogueform.text.h2": "Dialogue", + "_main__create.dialogueform.text.formitem": "Voice Tone", + "_main__create.dialogueform.text.formlabel": "Voice Tone", + "_main__create.dialogueform.placeholder.textarea_placeholder": "Please describe the way the characters chat, the tone of the dialogue", + "_main__create.dialogueform.text.span": "(Optional)", + "_main__create.dialogueform.text.label": "Suggestions", + "_main__create.dialogueform.text.tabs": "Female", + "_main__create.dialogueform.text.tabslist": "Female", + "_main__create.dialogueform.text.tabstrigger": "Female", + "_main__create.imageformschema.validation.message": "请上传或生成一张图片", + "_main__create.imageform.toast.toast_success": "Character created successfully!", + "_main__create.imageform.text.div": "Submit", + "_main__create.imageform.text.form": "Submit", + "_main__create.imageform.text.h2": "Image", + "_main__create.imageform.text.formitem": "Public", + "_main__create.imageform.text.formlabel": "Privacy", + "_main__create.imageform.alt.image_alt": "Avatar", + "_main__create.imageform.placeholder.textarea_placeholder": "Introduce the virtual character to the user", + "_main__create.imageform.text.chip": "Privacy", + "_main__create.imageform.text.button": "Submit", + "_main__create.imagegeneration.text.div": "Generating image...", + "_main__create.imagegeneration.text.h4": "Description", + "_main__create.imagegeneration.text.button": "Using Character Information", + "_main__create.imagegeneration.placeholder.textarea_placeholder": "Describe the character image you want to generate, for example: beautiful anime girl with long blue hair, wearing school uniform...", + "_main__create.imagegeneration.text.p": "Generating image...", + "_main__create.imagepreview.text.div": "Main image selected", + "_main__create.imagepreview.alt.image_alt": "Character image", + "_main__create.imagepreview.text.p": "Main image selected", + "_main__create.imageselector.text.div": "Click to Generate an Image", + "_main__create.imageupload.text.div": "files", + "_main__create.imageupload.text.p": "files", + "_main__create.typeform.text.div": "Tags", + "_main__create.typeform.text.form": "Tags", + "_main__create.typeform.text.h2": "Type", + "_main__create.typeform.text.formitem": "Tags", + "_main__create.typeform.text.formlabel": "Tags", + "_main__crushcoin.checkincard.text.div": "Not Started", + "_main__crushcoin.checkincard.alt.image_alt": "Star", + "_main__crushcoin.checkingrid.toast.toast_success": "Signed in today", + "_.genaralimagecard.text.div": "%...", + "_.genaralimagecard.alt.image_alt": "image", + "_.genaralimagecard.text.p": "%...", + "_.generalbuytimesdialog.text.alertdialog": "Continue", + "_.generalbuytimesdialog.text.alertdialogcontent": "Continue", + "_.generalbuytimesdialog.alt.image_alt": "diamond", + "_.generalbuytimesdialog.text.div": "Continue", + "_.generalbuytimesdialog.text.h2": "Buy Times", + "_.generalbuytimesdialog.text.span": "/time", + "_.generalbuytimesdialog.text.button": "Continue", + "_.renderunlocktext.text.div": "Unlock: Free", + "_.genaralimagecardmultiple.text.div": "Generating...", + "_.genaralimagecardmultiple.text.p": "Generating...", + "_.generalimagelist.text.div": "Your image will show here", + "_.generalimagelist.text.p": "Your image will show here", + "_.generalimagewithcountbutton.text.button": "Buy Times", + "_.generalimagewithcountbutton.alt.image_alt": "vip", + "_.renderpayaction.text.div": "Unlock Method: Free", + "_.multiplevieweraction.text.div": "Select", + "_.validatefile.toast.toast_error": "Image files cannot exceed 10MB.", + "_.referenceupload.alt.img_alt": "Reference", + "_.referenceupload.text.div": "Click or drag image here to Upload", + "_.referenceupload.text.p": "Click or drag image here to Upload", + "_main__generate_image.generateimageformschema.validation.message": "Please enter", + "_main__generate_image.generateimagepage.text.div": "Confirm", + "_main__generate_image.generateimagepage.text.form": "Generate", + "_main__generate_image.generateimagepage.text.h1": "Generate an Image", + "_main__generate_image.generateimagepage.text.formitem": "Prompt", + "_main__generate_image.generateimagepage.text.formlabel": "Prompt", + "_main__generate_image.generateimagepage.placeholder.textarea_placeholder": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "_main__generate_image.generateimagepage.text.button": "Confirm", + "_main__generate_image_2_background.generateimageformschema.validation.message": "Please enter", + "_main__generate_image_2_background.image2backgroundpage.text.div": "Confirm", + "_main__generate_image_2_background.image2backgroundpage.text.form": "AI will create based on the basic image of the character", + "_main__generate_image_2_background.image2backgroundpage.text.h1": "Generate an Image", + "_main__generate_image_2_background.image2backgroundpage.text.formitem": "Prompt", + "_main__generate_image_2_background.image2backgroundpage.text.formlabel": "Prompt", + "_main__generate_image_2_background.image2backgroundpage.placeholder.textarea_placeholder": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "_main__generate_image_2_background.image2backgroundpage.alt.image_alt": "Diamond", + "_main__generate_image_2_background.image2backgroundpage.text.button": "Confirm", + "_main__generate_image_2_image.generateimageformschema.validation.message": "Please enter", + "_main__generate_image_2_image.imagepage.text.div": "Confirm", + "_main__generate_image_2_image.imagepage.text.form": "AI will create based on the basic image of the character", + "_main__generate_image_2_image.imagepage.text.h1": "Generate an Image", + "_main__generate_image_2_image.imagepage.text.formitem": "Prompt", + "_main__generate_image_2_image.imagepage.text.formlabel": "Prompt", + "_main__generate_image_2_image.imagepage.placeholder.textarea_placeholder": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "_main__generate_image_2_image.imagepage.text.button": "Confirm", + "_main__generate_image_edit.generateimageformschema.validation.message": "Please enter", + "_main__generate_image_edit.imageeditpage.text.div": "Confirm", + "_main__generate_image_edit.imageeditpage.text.form": "AI will create based on the basic image of the character.", + "_main__generate_image_edit.imageeditpage.text.h1": "Generate an Image", + "_main__generate_image_edit.imageeditpage.text.formitem": "Prompt", + "_main__generate_image_edit.imageeditpage.text.formlabel": "Prompt", + "_main__generate_image_edit.imageeditpage.placeholder.textarea_placeholder": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "_main__generate_image_edit.imageeditpage.text.button": "Confirm", + "_.homeheader.text.div": "Daily Free Crush Coin", + "_.homeheader.text.link": "Daily Free Crush Coin", + "_.homeheader.alt.image_alt": "Banner", + "_.leaderboardbanner.text.div": "Gifts", + "_.leaderboardbanner.text.chip": "Gifts", + "_main__leaderboard.largerankcard.text.link": "1st", + "_main__leaderboard.largerankcard.text.div": "1st", + "_main__leaderboard.topheader.text.div": "No leaderboard data yet", + "_main__profile_account.handledisableaccount.toast.toast_success": "Account disabled successfully", + "_main__profile_account.accountpage.text.div": "Disable", + "_main__profile_account.accountpage.text.h1": "Account", + "_main__profile_account.accountpage.text.alertdialog": "Disable", + "_main__profile_account.accountpage.text.alertdialogtrigger": "Disable the Account", + "_main__profile_account.accountpage.text.button": "Disable the Account", + "_main__profile_account.accountpage.text.alertdialogcontent": "Disable", + "_main__profile_account.accountpage.text.alertdialogheader": "Disable the Account", + "_main__profile_account.accountpage.text.alertdialogtitle": "Disable the Account", + "_main__profile_account.accountpage.text.alertdialogdescription": "Are you sure you want to disable your account?", + "_main__profile_account.accountpage.text.alertdialogfooter": "Disable", + "_main__profile_account.accountpage.text.alertdialogcancel": "Cancel", + "_main__profile_account.accountpage.text.alertdialogaction": "Disable", + "_main__profile.handlefileupload.toast.toast_error": "Image files cannot exceed 10MB.", + "_main__profile.avatarsetting.text.div": "Upload", + "_main__profile.avatarsetting.text.p": "The avatar must be in JPG, JPEG, or PNG format, and the file size cannot exceed 10MB.", + "_main__profile.avatarsetting.text.button": "Upload", + "_main__profile.charactercardadd.text.div": "Add a Character", + "_main__profile.charactercardadd.alt.image_alt": "VIP", + "_main__profile.charactercardadd.text.span": "Add a Character", + "_main__profile.charactercardvipadd.text.div": "Add More Character", + "_main__profile.charactercardvipadd.text.span": "Add More Character", + "_main__profile.characterlist.text.div": "Unlock more", + "_main__profile.characterlist.text.h2": "Characters", + "_main__profile.characterlist.text.span": "Unlock more", + "_main__profile.characterlist.alt.img_alt": "VIP", + "_main__profile.profiledropdown.text.div": "Cancel", + "_main__profile.profiledropdown.text.dropdownmenu": "Log out", + "_main__profile.profiledropdown.text.dropdownmenucontent": "Log out", + "_main__profile.profiledropdown.text.link": "Privacy Policy", + "_main__profile.profiledropdown.text.profiledropdownitem": "Log out", + "_main__profile.profiledropdown.text.alertdialog": "Cancel", + "_main__profile.profiledropdown.text.alertdialogcontent": "Cancel", + "_main__profile.profiledropdown.text.alertdialogheader": "Log out", + "_main__profile.profiledropdown.text.alertdialogtitle": "Log out", + "_main__profile.profiledropdown.text.alertdialogdescription": "Are you sure you want to log out?", + "_main__profile.profiledropdown.text.alertdialogfooter": "Log out", + "_main__profile.profiledropdown.text.alertdialogcancel": "Cancel", + "_main__profile.profiledropdown.text.alertdialogaction": "Log out", + "_main__profile.profilefeaturelist.text.div": "Creator", + "_main__profile.profilefeaturelist.text.link": "Creator", + "_main__profile.profilefeaturelist.alt.image_alt": "Creator", + "_main__profile.profilefeaturelist.text.span": "Creator", + "_main__profile_edit.schema.validation.message": "Please select a gender", + "_main__profile_edit.onsubmit.error.form_seterror": "Nickname already exists", + "_main__profile_edit.onsubmit.validation.message": "Nickname already exists", + "_main__profile_edit.editpage.text.div": "Save", + "_main__profile_edit.editpage.text.h1": "Edit Profile", + "_main__profile_edit.editpage.text.form": "Save", + "_main__profile_edit.editpage.text.formitem": "Age", + "_main__profile_edit.editpage.text.formlabel": "Age", + "_main__profile_edit.editpage.placeholder.input_placeholder": "Placeholder", + "_main__profile_edit.editpage.text.p": "Can not edit after setting up", + "_main__profile_edit.editpage.placeholder.selectvalue_placeholder": "Day", + "_main__profile_edit.editpage.text.button": "Save", + "_main__user__userid_.notfound.title.empty_title": "Oops, there’s nothing here…", + "_main__wallet.incomeitem.text.div": "+", + "_main__wallet.incomeitem.alt.image_alt": "Diamond", + "_main__wallet.incomeemptystate.title.empty_title": "No income record", + "_main__wallet.incomelist.text.div": "Income Details", + "_main__wallet.incomelist.text.h2": "Income Details", + "_main__wallet.handlepayment.toast.toast_error": "Payment failure, please try again", + "_main__wallet.rechargelist.text.div": "Agreement", + "_main__wallet.rechargelist.text.h2": "Recharge", + "_main__wallet.rechargelist.alt.image_alt": "diamond", + "_main__wallet.rechargelist.text.span": "Agreement", + "_main__wallet.rechargelist.text.link": "Agreement", + "_main__wallet_transactions.transactionspage.text.div": "Transaction Details", + "_main__chat__aiid_.chatbackground.alt.image_alt": "Background", + "_main__chat__aiid_.chatmessageuserheader.text.div": "All responses are AI-Generated and fictional.", + "_main__chat__aiid_.crushlevelaction.alt.image_alt": "Heart", + "_main__create.voiceselector.text.div": "Tap to Listen", + "_main__create.voiceselector.label.voicetoneslider_label": "Speed", + "_main__create.voiceselector.text.span": "Tap to Listen", + "_main__edit__aiid__character.characterformschema.validation.message": "Please select gender", + "_main__edit__aiid__character.onsubmit.error.form_seterror": "Nickname already exists", + "_main__edit__aiid__character.onsubmit.validation.message": "Nickname already exists", + "_main__edit__aiid__character.characterform.text.div": "Next", + "_main__edit__aiid__character.characterform.text.h1": "Edit Character", + "_main__edit__aiid__character.characterform.text.form": "Next", + "_main__edit__aiid__character.characterform.text.h2": "Character", + "_main__edit__aiid__character.characterform.text.formitem": "Preferences", + "_main__edit__aiid__character.characterform.text.formlabel": "Preferences", + "_main__edit__aiid__character.characterform.placeholder.input_placeholder": "Enter character name", + "_main__edit__aiid__character.characterform.placeholder.selectvalue_placeholder": "Day", + "_main__edit__aiid__character.characterform.placeholder.textarea_placeholder": "Describe the character's background, personality, and identity", + "_main__edit__aiid__character.characterform.text.button": "Next", + "_main__edit__aiid_.closeiconbutton.text.alertdialog": "Quit", + "_main__edit__aiid_.closeiconbutton.text.alertdialogcontent": "Quit", + "_main__edit__aiid_.closeiconbutton.text.alertdialogheader": "The content has not been saved, Continue to quit?", + "_main__edit__aiid_.closeiconbutton.text.alertdialogtitle": "Unsaved changes", + "_main__edit__aiid_.closeiconbutton.text.alertdialogdescription": "The content has not been saved, Continue to quit?", + "_main__edit__aiid_.closeiconbutton.text.alertdialogfooter": "Quit", + "_main__edit__aiid_.closeiconbutton.text.alertdialogcancel": "Cancel", + "_main__edit__aiid_.closeiconbutton.text.alertdialogaction": "Quit", + "_main__edit__aiid__dialogue.dialogueformschema.validation.message": "Please select voice", + "_main__edit__aiid__dialogue.dialogueform.text.div": "Next", + "_main__edit__aiid__dialogue.dialogueform.text.h1": "Edit Character", + "_main__edit__aiid__dialogue.dialogueform.text.form": "Next", + "_main__edit__aiid__dialogue.dialogueform.text.h2": "Dialogue", + "_main__edit__aiid__dialogue.dialogueform.text.formitem": "Voice Tone", + "_main__edit__aiid__dialogue.dialogueform.text.formlabel": "Voice Tone", + "_main__edit__aiid__dialogue.dialogueform.placeholder.textarea_placeholder": "Please describe the way the characters chat, the tone of the dialogue", + "_main__edit__aiid__dialogue.dialogueform.text.span": "(Optional)", + "_main__edit__aiid__dialogue.dialogueform.text.label": "Suggestions", + "_main__edit__aiid__dialogue.dialogueform.text.tabs": "Female", + "_main__edit__aiid__dialogue.dialogueform.text.tabslist": "Female", + "_main__edit__aiid__dialogue.dialogueform.text.tabstrigger": "Female", + "_main__edit__aiid__dialogue.dialogueform.text.button": "Next", + "_main__edit__aiid__image.imageformschema.validation.message": "请上传或生成一张图片", + "_main__edit__aiid__image.imageform.text.div": "Submit", + "_main__edit__aiid__image.imageform.text.h1": "Edit Character", + "_main__edit__aiid__image.imageform.text.form": "Submit", + "_main__edit__aiid__image.imageform.text.h2": "Image", + "_main__edit__aiid__image.imageform.text.formitem": "Public", + "_main__edit__aiid__image.imageform.text.formlabel": "Privacy", + "_main__edit__aiid__image.imageform.alt.image_alt": "Avatar", + "_main__edit__aiid__image.imageform.placeholder.textarea_placeholder": "Introduce the virtual character to the user", + "_main__edit__aiid__image.imageform.text.chip": "Privacy", + "_main__edit__aiid__image.imageform.text.button": "Submit", + "_main__edit__aiid__type.typeform.text.div": "Next", + "_main__edit__aiid__type.typeform.text.h1": "Edit Character", + "_main__edit__aiid__type.typeform.text.form": "Next", + "_main__edit__aiid__type.typeform.text.h2": "Type", + "_main__edit__aiid__type.typeform.text.formitem": "Tags", + "_main__edit__aiid__type.typeform.text.formlabel": "Tags", + "_main__edit__aiid__type.typeform.text.button": "Next", + "_.filterdrawer.text.drawer": "Confirm", + "_.filterdrawer.text.drawercontent": "Confirm", + "_.filterdrawer.text.drawerheader": "Filter", + "_.filterdrawer.text.div": "Confirm", + "_.filterdrawer.text.drawertitle": "Filter", + "_.filterdrawer.text.h3": "Type", + "_.filterdrawer.text.button": "Confirm", + "_.meetcard.aria.div_aria_hidden": "true", + "_.meetheader.text.div": "#", + "_.meetheader.text.h1": "Meet", + "_.meetheader.text.tabs": "All", + "_.meetheader.text.tabslist": "All", + "_.meetheader.text.tabstrigger": "All", + "_.meetheader.text.span": "All", + "_.meetheader.text.chip": "#", + "_.errorstate.text.div": "Reload", + "_.errorstate.text.h3": "Load failed", + "_.errorstate.text.p": "The network connection is abnormal. Please try again later", + "_.errorstate.text.button": "Reload", + "_main__user__userid_.aboutsection.text.div": "Introduce", + "_main__user__userid_.aboutsection.text.h3": "Introduce", + "_main__user__userid_.renderpayaction.text.div": "Unlock Method: Free", + "_main__user__userid_.renderpayaction.alt.image_alt": "diamond", + "_main__user__userid_.renderdefaultaction.text.div": "Default", + "_main__user__userid_.renderdefaultaction.text.alertdialog": "Confirm", + "_main__user__userid_.renderdefaultaction.text.alertdialogcontent": "Confirm", + "_main__user__userid_.renderdefaultaction.text.alertdialogheader": "Default image", + "_main__user__userid_.renderdefaultaction.text.alertdialogtitle": "Default image", + "_main__user__userid_.renderdefaultaction.text.alertdialogdescription": "After setting as the default picture, the unlock method of the picture can only be \"free\".", + "_main__user__userid_.renderdefaultaction.text.alertdialogfooter": "Confirm", + "_main__user__userid_.renderdefaultaction.text.alertdialogcancel": "Cancel", + "_main__user__userid_.renderdefaultaction.text.button": "Confirm", + "_main__user__userid_.albumimagevieweraction.text.button": "Unlock", + "_main__user__userid_.albumimagevieweraction.text.div": "Like", + "_main__user__userid_.renderoverlay.text.div": "Unlock", + "_main__user__userid_.renderdefaulttag.text.tag": "Default", + "_main__user__userid_.albumitem.alt.image_alt": "Album image", + "_main__user__userid_.albumitemaction.text.dropdownmenu": "Delete", + "_main__user__userid_.albumitemaction.text.dropdownmenucontent": "Delete", + "_main__user__userid_.albumitemaction.text.albumdeletealert": "Delete", + "_main__user__userid_.albumitemaction.text.dropdownmenuitem": "Delete", + "_main__user__userid_.albumitemaction.text.span": "Delete", + "_main__user__userid_.handleunlock.toast.toast_success": "Unlocked successfully!", + "_main__user__userid_.albumlist.title.empty_title": "No photos yet", + "_main__user__userid_.giftgrid.text.div": "X", + "_main__user__userid_.giftgrid.text.h3": "Gift", + "_main__user__userid_.giftgrid.title.empty_title": "No gifts yet", + "_main__user__userid_.tabnavigation.text.div": "Album", + "_main__user__userid_.tabnavigation.text.button": "Album", + "_main__user__userid_.useractiondropdown.text.div": "Delete Character", + "_main__user__userid_.useractiondropdown.text.dropdownmenu": "Delete Character", + "_main__user__userid_.useractiondropdown.text.dropdownmenucontent": "Delete Character", + "_main__user__userid_.useractiondropdown.text.dropdownmenuitem": "Delete Character", + "_main__user__userid_.useractiondropdown.text.span": "Delete Character", + "_main__user__userid_.useractiondropdown.text.alertdialog": "Delete", + "_main__user__userid_.useractiondropdown.text.alertdialogcontent": "Delete", + "_main__user__userid_.useractiondropdown.text.alertdialogheader": "Delete Character", + "_main__user__userid_.useractiondropdown.text.alertdialogtitle": "Delete Character", + "_main__user__userid_.useractiondropdown.text.alertdialogdescription": "Are you sure you want to delete this character?", + "_main__user__userid_.useractiondropdown.text.alertdialogfooter": "Delete", + "_main__user__userid_.useractiondropdown.text.alertdialogaction": "Delete", + "_main__user__userid_.usercard.text.button": "Chat", + "_main__user__userid_.usercard.alt.image_alt": "Gender", + "_main__user__userid_.userprofiletabs.text.div": "Create a Photo", + "_main__user__userid_.userprofiletabs.text.tabslist": "Album", + "_main__user__userid_.userprofiletabs.text.tabstrigger": "Album", + "_main__user__userid_.userprofiletabs.text.span": "Album", + "_main__user__userid_.userprofiletabs.text.button": "Create a Photo", + "_main__user__userid_.usershare.text.dropdownmenu": "Share to X", + "_main__user__userid_.usershare.text.dropdownmenucontent": "Share to X", + "_main__user__userid_.usershare.text.dropdownmenuitem": "Share to X", + "_main__user__userid_.usershare.text.span": "Share to X", + "_main__vip.subscribetext.text.div": "$5.99/month", + "_main__vip.subscribetext.text.span": "$5.99/month", + "_main__vip.subscribeproductitem.text.div": ".", + "_main__vip.subscribevipdrawer.text.drawer": "Subscribe Now", + "_main__vip.subscribevipdrawer.text.drawercontent": "Subscribe Now", + "_main__vip.subscribevipdrawer.text.div": "CrushLevel Vip", + "_main__vip.subscribevipdrawer.text.drawerheader": "CrushLevel Vip", + "_main__vip.subscribevipdrawer.text.drawertitle": "CrushLevel Vip", + "_main__wallet_charge_result.resultpage.text.div": "Payment in progress, please do not leave the current page", + "_main__wallet_charge_result.resultpage.alt.image_alt": "pending", + "_main__wallet_charge_result.resultpage.text.button": ")", + "_main__wallet.handlewithdraw.dialog.alert": "No amount available for withdrawal", + "_main__wallet.incomecard.text.div": "Withdraw", + "_main__wallet.incomecard.alt.image_alt": "diamond-icon", + "_main__wallet.incomecard.text.tooltip": "Earnings can be withdrawn after 30 days", + "_main__wallet.incomecard.text.tooltipcontent": "Earnings can be withdrawn after 30 days", + "_main__wallet.incomecard.text.p": "Earnings can be withdrawn after 30 days", + "_main__wallet.incomecard.text.button": "Withdraw", + "_main__wallet.incomecard.text.span": "Withdraw", + "_main__wallet.rechargecard.text.div": "Crush Coin Balance", + "_main__wallet_transactions.diamondicon.alt.image_alt": "Diamond", + "_main__wallet_transactions.transactionemptystate.title.empty_title": "No transaction records", + "_main__chat__aiid_.renderaction.text.button": "Click to interrupt", + "_main__chat__aiid_.renderaction.text.div": "Listening...", + "_main__chat__aiid_.chatcallstatus.text.div": "Waiting to be connected", + "_main__chat__aiid_.backgroundimagevieweraction.text.div": "Select", + "_main__chat__aiid_.backgrounditem.text.div": "Default", + "_main__chat__aiid_.backgrounditem.text.tag": "Default", + "_main__chat__aiid_.chatbackgrounddrawer.text.inlinedrawer": "Confirm", + "_main__chat__aiid_.chatbackgrounddrawer.text.inlinedrawercontent": "Confirm", + "_main__chat__aiid_.chatbackgrounddrawer.text.inlinedrawerheader": "Chat Background", + "_main__chat__aiid_.chatbackgrounddrawer.text.inlinedrawerdescription": "Create Image", + "_main__chat__aiid_.chatbackgrounddrawer.text.div": "Create Image", + "_main__chat__aiid_.chatbackgrounddrawer.text.inlinedrawerfooter": "Confirm", + "_main__chat__aiid_.chatbackgrounddrawer.text.button": "Confirm", + "_main__chat__aiid_.chatbuttleitem.text.div": "Hi", + "_main__chat__aiid_.chatbuttleitem.text.chatbubble": "Hi", + "_main__chat__aiid_.renderconfirmbutton.text.button": "Unlock", + "_main__chat__aiid_.renderconfirmbutton.text.div": "Unlock", + "_main__chat__aiid_.renderconfirmbutton.alt.image_alt": "vip", + "_main__chat__aiid_.renderconfirmbutton.text.span": "Unlock", + "_main__chat__aiid_.chatbuttledrawer.text.inlinedrawer": "Chat Buttle", + "_main__chat__aiid_.chatbuttledrawer.text.inlinedrawercontent": "Chat Buttle", + "_main__chat__aiid_.chatbuttledrawer.text.inlinedrawerheader": "Chat Buttle", + "_main__chat__aiid_.chatmodeldrawer.text.inlinedrawer": "Save", + "_main__chat__aiid_.chatmodeldrawer.text.inlinedrawercontent": "Save", + "_main__chat__aiid_.chatmodeldrawer.text.inlinedrawerheader": "Chat Model", + "_main__chat__aiid_.chatmodeldrawer.text.inlinedrawerdescription": "/Voice call message", + "_main__chat__aiid_.chatmodeldrawer.text.div": "/Voice call message", + "_main__chat__aiid_.chatmodeldrawer.text.tooltip": "Voice Call Message price refers to the cost of having a voice call conversation with a character. It is charged per message.", + "_main__chat__aiid_.chatmodeldrawer.text.tooltipcontent": "Voice Call Message price refers to the cost of having a voice call conversation with a character. It is charged per message.", + "_main__chat__aiid_.chatmodeldrawer.text.p": "Voice Call Message price refers to the cost of having a voice call conversation with a character. It is charged per message.", + "_main__chat__aiid_.chatmodeldrawer.alt.image_alt": "diamond", + "_main__chat__aiid_.chatmodeldrawer.text.span": "/Voice call message", + "_main__chat__aiid_.chatmodeldrawer.text.inlinedrawerfooter": "Save", + "_main__chat__aiid_.chatmodeldrawer.text.button": "Save", + "_main__chat__aiid_.characterformschema.validation.message": "Please select gender", + "_main__chat__aiid_.onsubmit.error.form_seterror": "Nickname already exists", + "_main__chat__aiid_.onsubmit.validation.message": "Nickname already exists", + "_main__chat__aiid_.chatprofileeditdrawer.text.inlinedrawer": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.text.inlinedrawercontent": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.text.inlinedrawerheader": "My Chat Personal", + "_main__chat__aiid_.chatprofileeditdrawer.text.inlinedrawerdescription": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.text.form": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.text.formitem": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.text.formlabel": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.placeholder.input_placeholder": "Enter nickname", + "_main__chat__aiid_.chatprofileeditdrawer.text.div": "Birthday", + "_main__chat__aiid_.chatprofileeditdrawer.text.label": "Birthday", + "_main__chat__aiid_.chatprofileeditdrawer.placeholder.selectvalue_placeholder": "Day", + "_main__chat__aiid_.chatprofileeditdrawer.text.span": "(Optional)", + "_main__chat__aiid_.chatprofileeditdrawer.placeholder.textarea_placeholder": "Describe the character's background and personality traits.", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialog": "Exit", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogcontent": "Exit", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogheader": "The edited content will not be saved after exiting. Please confirm whether to continue exiting?", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogtitle": "Unsaved Edits", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogdescription": "The edited content will not be saved after exiting. Please confirm whether to continue exiting?", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogfooter": "Exit", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogcancel": "Cancel", + "_main__chat__aiid_.chatprofileeditdrawer.text.alertdialogaction": "Exit", + "_main__chat__aiid_.crushlevelretrievedrawer.text.inlinedrawer": "Purchase", + "_main__chat__aiid_.crushlevelretrievedrawer.text.inlinedrawercontent": "Purchase", + "_main__chat__aiid_.crushlevelretrievedrawer.alt.image_alt": "Crush Level", + "_main__chat__aiid_.crushlevelretrievedrawer.text.div": "Purchase", + "_main__chat__aiid_.crushlevelretrievedrawer.text.inlinedrawerheader": "Retrieve", + "_main__chat__aiid_.crushlevelretrievedrawer.text.inlinedrawerdescription": "Total", + "_main__chat__aiid_.crushlevelretrievedrawer.text.inlinedrawerfooter": "Purchase", + "_main__chat__aiid_.crushlevelretrievedrawer.text.button": "Purchase", + "_main__chat__aiid_.handlesendgift.toast.toast_error": "Gift sending failed. Please try again.", + "_main__chat__aiid_.getbutton.text.button": "Gift", + "_main__chat__aiid_.sendgiftsdrawer.text.inlinedrawer": "Balance:", + "_main__chat__aiid_.sendgiftsdrawer.text.inlinedrawercontent": "Balance:", + "_main__chat__aiid_.sendgiftsdrawer.text.inlinedrawerheader": "Send Gifts", + "_main__chat__aiid_.sendgiftsdrawer.text.inlinedrawerfooter": "Balance:", + "_main__chat__aiid_.sendgiftsdrawer.text.div": "Balance:", + "_main__chat__aiid_.sendgiftsdrawer.text.span": "Balance:", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialog": "Go", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogcontent": "Go", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogheader": "Create Album", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogtitle": "Create Album", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogdescription": "Generate images for your character's album to attract fans and earn revenue.", + "_main__chat__aiid_.chatfirstguidedialog.text.p": "Generate images for your character's album to attract fans and earn revenue.", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogfooter": "Go", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogcancel": "Not now", + "_main__chat__aiid_.chatfirstguidedialog.text.alertdialogaction": "Go", + "_main__chat__aiid_.aireplysuggestions.text.div": "/", + "_main__chat__aiid_.aireplysuggestions.text.p": "Choose one or edit", + "_main__chat__aiid_.aireplysuggestions.text.span": "/", + "_main__chat__aiid_.chatactionplus.text.dropdownmenu": "Share to X", + "_main__chat__aiid_.chatactionplus.text.dropdownmenucontent": "Share to X", + "_main__chat__aiid_.chatactionplus.text.dropdownmenuitem": "Share to X", + "_main__chat__aiid_.chatactionplus.text.span": "Share to X", + "_main__chat__aiid_.chatactionplus.text.div": "Share to", + "_main__chat__aiid_._______uploading__imageuploading_______uploadfile_______cancelupload______error__uploaderror______progress__uploadprogress_____.validation.message": "图片上传失败,请重试", + "_main__chat__aiid_.handleimageselect.validation.message": "图片处理失败,请重试", + "_main__chat__aiid_.handlevoicerecord.toast.toast_error": "Voice too short", + "_main__chat__aiid_.chatmessageaction.placeholder.chatinput_placeholder": "Enter some thing here", + "_main__chat__aiid_.debouncedfeedback.toast.toast_error": "Operation failed, please try again", + "_main__chat__aiid_.handlecopy.toast.toast_success": "Copied to clipboard", + "_main__chat__aiid_.chatothertextcontainer.text.div": "Bad", + "_main__chat__aiid_.chatothertextcontainer.text.span": "|", + "_main__chat__aiid_.chatothertextcontainer.text.tooltip": "Bad", + "_main__chat__aiid_.chatothertextcontainer.text.tooltipcontent": "Bad", + "_main__chat__aiid_.chatothertextcontainer.text.p": "Bad", + "_main__chat__aiid_.chatusertextcontainer.alt.image_alt": "Sending-failed", + "_main__chat__aiid_.crushlevelavatar.text.div": "/profile", + "_main__chat__aiid_.crushlevelavatar.alt.image_alt": "heart", + "_main__chat__aiid_.crushlevelavatar.text.link": "/profile", + "_main__chat__aiid_.chatprofileaction.text.div": "Delete", + "_main__chat__aiid_.chatprofileaction.text.dropdownmenu": "Delete", + "_main__chat__aiid_.chatprofileaction.text.dropdownmenucontent": "Delete", + "_main__chat__aiid_.chatprofileaction.text.dropdownmenuitem": "Delete", + "_main__chat__aiid_.chatprofileaction.text.span": "Delete", + "_main__chat__aiid_.chatprofilepersona.text.div": "Who i am", + "_main__chat__aiid_.deletemessagedialog.text.alertdialog": "Deletion is permanent. Your accumulated Affection points and the character's memories will not be affected. Please confirm deletion.", + "_main__chat__aiid_.deletemessagedialog.text.alertdialogcontent": "Deletion is permanent. Your accumulated Affection points and the character's memories will not be affected. Please confirm deletion.", + "_main__chat__aiid_.deletemessagedialog.text.alertdialogheader": "Delete", + "_main__chat__aiid_.deletemessagedialog.text.alertdialogtitle": "Delete", + "_main__chat__aiid_.deletemessagedialog.text.alertdialogdescription": "Deletion is permanent. Your accumulated Affection points and the character's memories will not be affected. Please confirm deletion.", + "_main__chat__aiid_.deletemessagedialog.text.alertdialogfooter": "Delete", + "_main__chat__aiid_.deletemessagedialog.text.alertdialogaction": "Delete", + "_main__chat__aiid_.chatprofiledrawer.text.inlinedrawer": "Auto play voice", + "_main__chat__aiid_.chatprofiledrawer.text.inlinedrawercontent": "Auto play voice", + "_main__chat__aiid_.chatprofiledrawer.text.div": "Auto play voice", + "_main__chat__aiid_.chatprofiledrawer.alt.image_alt": "Gender", + "_main__chat__aiid_.renderlinetext.text.div": "·", + "_main__chat__aiid_.renderlinetext.text.span": "·", + "_main__chat__aiid_.crushleveldrawer.text.inlinedrawer": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.inlinedrawercontent": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.div": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.inlinedrawerheader": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.tooltip": "* The crush value will increase the crush level, and unlock titles, functions, and different character dialogue stages through upgrades", + "_main__chat__aiid_.crushleveldrawer.text.tooltipcontent": "* The crush value will increase the crush level, and unlock titles, functions, and different character dialogue stages through upgrades", + "_main__chat__aiid_.crushleveldrawer.text.p": "* The crush value will increase the crush level, and unlock titles, functions, and different character dialogue stages through upgrades", + "_main__chat__aiid_.crushleveldrawer.text.dropdownmenu": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.dropdownmenucontent": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.dropdownmenuitem": "Hide Relationship", + "_main__chat__aiid_.crushleveldrawer.text.span": "℃", + "_main__chat__aiid_.crushleveldrawer.text.button": "Retrieve", + "_main__chat__aiid_.callcancelitem.text.div": "Call Canceled", + "_main__chat__aiid_.chatimagecontainer.text.div": "×", + "_main__chat__aiid_.handleunlock.toast.toast_success": "Unlock success", + "_main__chat__aiid_.handleunlock.toast.toast_error": "Unlock failed" +} \ No newline at end of file diff --git a/public/login-image.png b/public/login-image.png new file mode 100644 index 0000000..eec8944 Binary files /dev/null and b/public/login-image.png differ diff --git a/public/login-logo.svg b/public/login-logo.svg new file mode 100644 index 0000000..57c0a3b --- /dev/null +++ b/public/login-logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..7f0a69d --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js new file mode 100644 index 0000000..be4527c --- /dev/null +++ b/public/mockServiceWorker.js @@ -0,0 +1,344 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = '2.10.4' +const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +addEventListener('install', function () { + self.skipWaiting() +}) + +addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +addEventListener('message', async function (event) { + const clientId = Reflect.get(event.source || {}, 'id') + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +addEventListener('fetch', function (event) { + // Bypass navigation requests. + if (event.request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) + +/** + * @param {FetchEvent} event + * @param {string} requestId + */ +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const requestCloneForEvents = event.request.clone() + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents) + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ) + } + + return response +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @returns {Promise} + */ +async function getResponse(event, client, requestId) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request) + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) + }) +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + } +} diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/voice/connecting.mp3 b/public/voice/connecting.mp3 new file mode 100644 index 0000000..0e83065 Binary files /dev/null and b/public/voice/connecting.mp3 differ diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/TRANSLATION_SUMMARY.md b/scripts/TRANSLATION_SUMMARY.md new file mode 100644 index 0000000..dfb2753 --- /dev/null +++ b/scripts/TRANSLATION_SUMMARY.md @@ -0,0 +1,70 @@ +# 文案翻译覆盖总结 + +## 执行结果 + +✅ **成功完成文案翻译覆盖任务** + +### 统计数据 +- **总翻译条目**: 378 条(去重后) +- **成功替换**: 334 条 +- **成功率**: 88.4% +- **剩余冲突**: 44 条 + +### 冲突分析 +- **文本未找到**: 24 条 - 主要是包含特殊字符(如 emoji)的文本 +- **多处匹配**: 20 条 - 同一文本在文件中出现多次,需要人工确认 + +## 实现的功能 + +### 1. 智能文案替换 +- 基于 `ts-morph` AST 解析,精确定位不同类型的文案 +- 支持 JSX 文本、属性值、函数参数等多种文案类型 +- 保持代码格式和缩进不变 + +### 2. 冲突检测机制 +- 自动检测文件不存在、文本未找到、多处匹配等冲突 +- 生成详细的冲突报告,便于人工处理 + +### 3. 去重处理 +- 自动去除翻译数据中的重复条目 +- 避免重复替换导致的错误 + +### 4. 报告生成 +- 成功替换报告:`scripts/translation-report.json` +- 冲突报告:`scripts/translation-conflicts.xlsx` + +## 使用的脚本 + +### 主要脚本 +- `scripts/apply-translations.cjs` - 基础翻译应用脚本 +- `scripts/reset-and-apply-translations.cjs` - 重置并应用翻译脚本(推荐使用) + +### 使用方法 +```bash +# 重置文件并应用翻译(推荐) +node scripts/reset-and-apply-translations.cjs + +# 仅应用翻译(如果文件未被修改过) +node scripts/apply-translations.cjs +``` + +## 处理建议 + +### 对于剩余冲突 +1. **文本未找到的条目**:检查是否包含特殊字符或格式问题 +2. **多处匹配的条目**:需要人工确认具体替换哪个位置 + +### 后续优化 +1. 可以针对特殊字符(emoji)的匹配进行优化 +2. 可以添加更智能的多处匹配处理逻辑 +3. 可以添加翻译质量验证机制 + +## 文件变更 + +所有成功替换的文案已直接修改到源代码文件中,包括: +- React 组件中的 JSX 文本 +- 属性值(title、placeholder、alt 等) +- 函数调用中的字符串参数 +- 表单验证消息等 + +翻译覆盖任务已成功完成!🎉 diff --git a/scripts/apply-translations.cjs b/scripts/apply-translations.cjs new file mode 100644 index 0000000..419f2d3 --- /dev/null +++ b/scripts/apply-translations.cjs @@ -0,0 +1,382 @@ +/* + CommonJS runtime for applying translations from Excel to source code. +*/ +const path = require('node:path'); +const fs = require('node:fs'); +const { Project, SyntaxKind, Node } = require('ts-morph'); +const XLSX = require('xlsx'); + +const WORKDIR = process.cwd(); +const TRANSLATES_FILE = path.join(WORKDIR, 'scripts', 'translates.xlsx'); +const REPORT_FILE = path.join(WORKDIR, 'scripts', 'translation-report.json'); +const CONFLICTS_FILE = path.join(WORKDIR, 'scripts', 'translation-conflicts.xlsx'); + +// 统计信息 +const stats = { + total: 0, + success: 0, + conflicts: 0, + fileNotFound: 0, + textNotFound: 0, + multipleMatches: 0 +}; + +// 冲突列表 +const conflicts = []; + +// 成功替换列表 +const successfulReplacements = []; + +function loadTranslations() { + console.log('📖 读取翻译数据...'); + const wb = XLSX.readFile(TRANSLATES_FILE); + const ws = wb.Sheets[wb.SheetNames[0]]; + const data = XLSX.utils.sheet_to_json(ws, { defval: '' }); + + // 筛选出需要替换的条目 + let translations = data.filter(row => + row.text && + row.corrected_text && + row.text !== row.corrected_text + ); + + // 去重:按 file + line + text 去重,保留第一个 + const seen = new Set(); + translations = translations.filter(row => { + const key = `${row.file}:${row.line}:${row.text}`; + if (seen.has(key)) { + return false; + } + seen.add(key); + return true; + }); + + console.log(`📊 找到 ${translations.length} 条需要替换的翻译(已去重)`); + stats.total = translations.length; + + return translations; +} + +function groupByFile(translations) { + const groups = new Map(); + for (const translation of translations) { + const filePath = path.join(WORKDIR, translation.file); + if (!groups.has(filePath)) { + groups.set(filePath, []); + } + groups.get(filePath).push(translation); + } + return groups; +} + +function findTextInNode(node, targetText, kind) { + if (!node) return null; + + // 处理 JSX 文本节点 + if (Node.isJsxText(node)) { + const text = node.getText().replace(/\s+/g, ' ').trim(); + if (text === targetText) return node; + } + + // 处理字符串字面量 + if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) { + const text = node.getLiteralText(); + if (text === targetText) return node; + } + + // 处理 JSX 表达式中的字符串 + if (Node.isJsxExpression(node)) { + const expr = node.getExpression(); + if (expr && (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr))) { + const text = expr.getLiteralText(); + if (text === targetText) return node; + } + } + + return null; +} + +function findTextInFile(sourceFile, translation) { + const { text, line, kind } = translation; + const matches = []; + + sourceFile.forEachDescendant((node) => { + // 根据 kind 类型进行不同的匹配 + if (kind === 'text') { + // 查找 JSX 文本节点 + if (Node.isJsxText(node)) { + const nodeText = node.getText().replace(/\s+/g, ' ').trim(); + if (nodeText === text) { + matches.push({ node, type: 'jsx-text', line: node.getStartLineNumber() }); + } + } + // 查找 JSX 表达式中的字符串 + if (Node.isJsxExpression(node)) { + const expr = node.getExpression(); + if (expr && (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr))) { + const nodeText = expr.getLiteralText(); + if (nodeText === text) { + matches.push({ node: expr, type: 'jsx-expression', line: node.getStartLineNumber() }); + } + } + } + } else if (['placeholder', 'title', 'alt', 'label', 'aria'].includes(kind)) { + // 查找 JSX 属性 + if (Node.isJsxAttribute(node)) { + const name = node.getNameNode().getText().toLowerCase(); + const value = getStringFromInitializer(node); + if (value === text) { + matches.push({ node, type: 'jsx-attribute', line: node.getStartLineNumber() }); + } + } + } else if (['toast', 'dialog', 'error', 'validation'].includes(kind)) { + // 查找函数调用中的字符串参数 + if (Node.isCallExpression(node)) { + const args = node.getArguments(); + for (const arg of args) { + if (Node.isStringLiteral(arg) || Node.isNoSubstitutionTemplateLiteral(arg)) { + const nodeText = arg.getLiteralText(); + if (nodeText === text) { + matches.push({ node: arg, type: 'function-arg', line: node.getStartLineNumber() }); + } + } + } + } + } + }); + + return matches; +} + +function getStringFromInitializer(attr) { + const init = attr.getInitializer(); + if (!init) return undefined; + if (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init)) { + return init.getLiteralText(); + } + if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (!expr) return undefined; + if (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr)) { + return expr.getLiteralText(); + } + } + return undefined; +} + +function replaceText(node, newText, type) { + try { + if (type === 'jsx-text') { + // JSX 文本节点需要特殊处理,保持空白字符 + const originalText = node.getText(); + const newTextWithWhitespace = originalText.replace(/\S+/g, newText); + node.replaceWithText(newTextWithWhitespace); + } else if (type === 'jsx-expression' || type === 'function-arg') { + // 字符串字面量 + if (Node.isStringLiteral(node)) { + node.replaceWithText(`"${newText}"`); + } else if (Node.isNoSubstitutionTemplateLiteral(node)) { + node.replaceWithText(`\`${newText}\``); + } + } else if (type === 'jsx-attribute') { + // JSX 属性值 + const init = node.getInitializer(); + if (init) { + if (Node.isStringLiteral(init)) { + init.replaceWithText(`"${newText}"`); + } else if (Node.isNoSubstitutionTemplateLiteral(init)) { + init.replaceWithText(`\`${newText}\``); + } else if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (expr && (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr))) { + if (Node.isStringLiteral(expr)) { + expr.replaceWithText(`"${newText}"`); + } else { + expr.replaceWithText(`\`${newText}\``); + } + } + } + } + } + return true; + } catch (error) { + console.error(`❌ 替换失败: ${error.message}`); + return false; + } +} + +function processFile(filePath, translations) { + if (!fs.existsSync(filePath)) { + console.log(`❌ 文件不存在: ${path.relative(WORKDIR, filePath)}`); + translations.forEach(t => { + conflicts.push({ + ...t, + conflictType: 'FILE_NOT_FOUND', + conflictReason: '文件不存在' + }); + }); + stats.fileNotFound += translations.length; + return; + } + + console.log(`📝 处理文件: ${path.relative(WORKDIR, filePath)}`); + + try { + const project = new Project({ + tsConfigFilePath: path.join(WORKDIR, 'tsconfig.json'), + skipAddingFilesFromTsConfig: true + }); + const sourceFile = project.addSourceFileAtPath(filePath); + + for (const translation of translations) { + const { text, corrected_text, line, kind } = translation; + + // 首先在指定行附近查找 + let matches = findTextInFile(sourceFile, translation); + + // 如果没找到,在整个文件中搜索 + if (matches.length === 0) { + matches = findTextInFile(sourceFile, translation); + } + + if (matches.length === 0) { + conflicts.push({ + ...translation, + conflictType: 'TEXT_NOT_FOUND_IN_FILE', + conflictReason: '在文件中找不到匹配的文本' + }); + stats.textNotFound++; + continue; + } + + if (matches.length > 1) { + conflicts.push({ + ...translation, + conflictType: 'MULTIPLE_MATCHES', + conflictReason: `找到 ${matches.length} 个匹配项,需要人工确认` + }); + stats.multipleMatches++; + continue; + } + + // 执行替换 + const match = matches[0]; + const success = replaceText(match.node, corrected_text, match.type); + + if (success) { + successfulReplacements.push({ + ...translation, + actualLine: match.line, + replacementType: match.type + }); + stats.success++; + console.log(`✅ 替换成功: "${text}" -> "${corrected_text}" (行 ${match.line})`); + } else { + conflicts.push({ + ...translation, + conflictType: 'REPLACEMENT_FAILED', + conflictReason: '替换操作失败' + }); + stats.conflicts++; + } + } + + // 保存修改后的文件 + sourceFile.saveSync(); + + } catch (error) { + console.error(`❌ 处理文件失败: ${filePath}`, error.message); + translations.forEach(t => { + conflicts.push({ + ...t, + conflictType: 'PARSE_ERROR', + conflictReason: `文件解析失败: ${error.message}` + }); + }); + stats.conflicts += translations.length; + } +} + +function generateReport() { + console.log('\n📊 生成报告...'); + + // 生成成功替换报告 + const report = { + timestamp: new Date().toISOString(), + stats, + successfulReplacements, + conflicts: conflicts.map(c => ({ + file: c.file, + line: c.line, + text: c.text, + corrected_text: c.corrected_text, + conflictType: c.conflictType, + conflictReason: c.conflictReason + })) + }; + + fs.writeFileSync(REPORT_FILE, JSON.stringify(report, null, 2)); + console.log(`📄 成功替换报告已保存: ${REPORT_FILE}`); + + // 生成冲突报告 Excel + if (conflicts.length > 0) { + const conflictRows = conflicts.map(c => ({ + file: c.file, + line: c.line, + text: c.text, + corrected_text: c.corrected_text, + conflictType: c.conflictType, + conflictReason: c.conflictReason, + route: c.route, + componentOrFn: c.componentOrFn, + kind: c.kind, + keyOrLocator: c.keyOrLocator + })); + + const ws = XLSX.utils.json_to_sheet(conflictRows); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'conflicts'); + XLSX.writeFile(wb, CONFLICTS_FILE); + console.log(`📄 冲突报告已保存: ${CONFLICTS_FILE}`); + } +} + +function printSummary() { + console.log('\n📈 处理完成!'); + console.log(`总翻译条目: ${stats.total}`); + console.log(`✅ 成功替换: ${stats.success}`); + console.log(`❌ 文件不存在: ${stats.fileNotFound}`); + console.log(`❌ 文本未找到: ${stats.textNotFound}`); + console.log(`❌ 多处匹配: ${stats.multipleMatches}`); + console.log(`❌ 其他冲突: ${stats.conflicts}`); + console.log(`\n成功率: ${((stats.success / stats.total) * 100).toFixed(1)}%`); +} + +async function main() { + console.log('🚀 开始应用翻译...\n'); + + try { + // 1. 读取翻译数据 + const translations = loadTranslations(); + + // 2. 按文件分组 + const fileGroups = groupByFile(translations); + + // 3. 处理每个文件 + for (const [filePath, fileTranslations] of fileGroups) { + processFile(filePath, fileTranslations); + } + + // 4. 生成报告 + generateReport(); + + // 5. 打印总结 + printSummary(); + + } catch (error) { + console.error('❌ 执行失败:', error); + process.exitCode = 1; + } +} + +main(); diff --git a/scripts/convert-to-i18n.js b/scripts/convert-to-i18n.js new file mode 100644 index 0000000..5cd5e37 --- /dev/null +++ b/scripts/convert-to-i18n.js @@ -0,0 +1,105 @@ +/* + 将现有的 copy-audit.xlsx 转换为 i18next 格式的翻译文件 +*/ + +const fs = require('fs'); +const path = require('path'); +const XLSX = require('xlsx'); + +const WORKDIR = process.cwd(); + +function generateI18nKey(item) { + // 生成 i18next 格式的键名 + const route = item.route === "shared" ? "common" : item.route.replace(/[^a-zA-Z0-9]/g, "_"); + const component = item.componentOrFn.replace(/[^a-zA-Z0-9]/g, "_"); + const kind = item.kind; + const locator = item.keyOrLocator.replace(/[^a-zA-Z0-9]/g, "_"); + + return `${route}.${component}.${kind}.${locator}`.toLowerCase(); +} + +function main() { + // 读取现有的 Excel 文件 + const excelFile = path.join(WORKDIR, 'docs', 'copy-audit.xlsx'); + if (!fs.existsSync(excelFile)) { + console.error('❌ 找不到 copy-audit.xlsx 文件,请先运行 extract-copy 脚本'); + process.exit(1); + } + + const workbook = XLSX.readFile(excelFile); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const data = XLSX.utils.sheet_to_json(worksheet); + + console.log(`📊 读取到 ${data.length} 条记录`); + + // 生成 i18next 格式的翻译文件 + const translation = {}; + const i18nKeys = []; + + data.forEach((item, index) => { + if (item.text && item.text.trim()) { + const key = generateI18nKey(item); + translation[key] = item.text; + i18nKeys.push({ + key, + value: item.text, + route: item.route, + file: item.file, + line: item.line, + kind: item.kind + }); + } + }); + + // 确保目录存在 + const localesDir = path.join(WORKDIR, 'public', 'locales', 'en'); + if (!fs.existsSync(localesDir)) { + fs.mkdirSync(localesDir, { recursive: true }); + } + + // 写入翻译文件 + const translationFile = path.join(localesDir, 'translation.json'); + fs.writeFileSync(translationFile, JSON.stringify(translation, null, 2)); + + // 生成详细的 i18n 扫描报告 + const report = { + totalItems: data.length, + uniqueTexts: new Set(data.map(item => item.text)).size, + translationKeys: Object.keys(translation).length, + byRoute: data.reduce((acc, item) => { + acc[item.route] = (acc[item.route] || 0) + 1; + return acc; + }, {}), + byKind: data.reduce((acc, item) => { + acc[item.kind] = (acc[item.kind] || 0) + 1; + return acc; + }, {}), + sampleKeys: Object.keys(translation).slice(0, 10) + }; + + // 生成 Excel 格式的 i18n 报告 + const i18nWorkbook = XLSX.utils.book_new(); + const i18nSheet = XLSX.utils.json_to_sheet(i18nKeys); + XLSX.utils.book_append_sheet(i18nWorkbook, i18nSheet, 'i18n-keys'); + + const i18nReportFile = path.join(WORKDIR, 'docs', 'i18n-scan-report.xlsx'); + XLSX.writeFile(i18nWorkbook, i18nReportFile); + + // 生成 JSON 报告 + const reportFile = path.join(WORKDIR, 'docs', 'i18n-scan-report.json'); + fs.writeFileSync(reportFile, JSON.stringify(report, null, 2)); + + console.log('✅ i18next 扫描转换完成!'); + console.log(`📊 总扫描条目: ${data.length}`); + console.log(`🔑 生成翻译键: ${Object.keys(translation).length}`); + console.log(`📁 翻译文件: ${translationFile}`); + console.log(`📋 i18n Excel 报告: ${i18nReportFile}`); + console.log(`📄 JSON 报告: ${reportFile}`); + console.log('\n📝 示例翻译键:'); + report.sampleKeys.forEach(key => { + console.log(` ${key}: "${translation[key]}"`); + }); +} + +main(); diff --git a/scripts/extract-copy.cjs b/scripts/extract-copy.cjs new file mode 100644 index 0000000..2415274 --- /dev/null +++ b/scripts/extract-copy.cjs @@ -0,0 +1,284 @@ +/* + CommonJS runtime for extracting user-facing copy into Excel. +*/ +const path = require('node:path'); +const fs = require('node:fs'); +const { globby } = require('globby'); +const { Project, SyntaxKind, Node } = require('ts-morph'); +const XLSX = require('xlsx'); + +const WORKDIR = process.cwd(); +const SRC_DIR = path.join(WORKDIR, 'src'); +const APP_DIR = path.join(SRC_DIR, 'app'); + +function ensureExcelDir() { + const docsDir = path.join(WORKDIR, 'docs'); + if (!fs.existsSync(docsDir)) fs.mkdirSync(docsDir, { recursive: true }); +} + +function isMeaningfulText(value) { + if (!value) return false; + const trimmed = String(value).replace(/\s+/g, ' ').trim(); + if (!trimmed) return false; + if (/^[A-Za-z0-9_.$-]+$/.test(trimmed) && trimmed.length > 24) return false; + return true; +} + +function getRouteForFile(absFilePath) { + if (!absFilePath.startsWith(APP_DIR)) return 'shared'; + let dir = path.dirname(absFilePath); + while (dir.startsWith(APP_DIR)) { + const pageTsx = path.join(dir, 'page.tsx'); + const pageTs = path.join(dir, 'page.ts'); + if (fs.existsSync(pageTsx) || fs.existsSync(pageTs)) { + const rel = path.relative(APP_DIR, dir); + return rel || '/'; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + const relToApp = path.relative(APP_DIR, absFilePath); + const parts = relToApp.split(path.sep); + return parts.length > 0 ? parts[0] : 'shared'; +} + +function getComponentOrFnName(node) { + const fn = node.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration); + if (fn && fn.getName && fn.getName()) return fn.getName(); + const varDecl = node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration); + if (varDecl && varDecl.getName) return varDecl.getName(); + const cls = node.getFirstAncestorByKind(SyntaxKind.ClassDeclaration); + if (cls && cls.getName && cls.getName()) return cls.getName(); + const sf = node.getSourceFile(); + return path.basename(sf.getFilePath()); +} + +function getNodeLine(node) { + const pos = node.getStartLineNumber && node.getStartLineNumber(); + return pos || 1; +} + +function getAttrName(attr) { + return attr.getNameNode().getText(); +} + +function getStringFromInitializer(attr) { + const init = attr.getInitializer(); + if (!init) return undefined; + if (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init)) return init.getLiteralText(); + if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (!expr) return undefined; + if (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr)) return expr.getLiteralText(); + } + return undefined; +} + +async function collectFiles() { + const patterns = ['src/**/*.{ts,tsx}']; + const ignore = ['**/node_modules/**','**/.next/**','**/__tests__/**','**/mocks/**','**/mock/**','**/*.d.ts']; + return await globby(patterns, { gitignore: true, ignore }); +} + +function pushItem(items, item) { + if (!isMeaningfulText(item.text)) return; + items.push(item); +} + +function extractFromSourceFile(abs, items, project) { + const sf = project.addSourceFileAtPath(abs); + sf.forEachDescendant((node) => { + // JSX text nodes + if (Node.isJsxElement(node)) { + const opening = node.getOpeningElement(); + const componentOrFn = getComponentOrFnName(node); + const route = getRouteForFile(abs); + const tagName = opening.getTagNameNode().getText(); + // 递归抓取所有子层级文本节点 + const textNodes = node.getDescendantsOfKind(SyntaxKind.JsxText); + textNodes.forEach((t) => { + const text = t.getText(); + const cleaned = text.replace(/\s+/g, ' ').trim(); + if (isMeaningfulText(cleaned)) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: 'text', + keyOrLocator: tagName, + text: cleaned, + line: getNodeLine(t), + }); + } + }); + // 抓取 {'...'} 这类表达式中的字符串字面量 + const exprs = node.getDescendantsOfKind(SyntaxKind.JsxExpression); + exprs.forEach((expr) => { + const inner = expr.getExpression(); + if (inner && (Node.isStringLiteral(inner) || Node.isNoSubstitutionTemplateLiteral(inner))) { + const cleaned = inner.getLiteralText().replace(/\s+/g, ' ').trim(); + if (isMeaningfulText(cleaned)) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: 'text', + keyOrLocator: tagName, + text: cleaned, + line: getNodeLine(expr), + }); + } + } + }); + } + + // JSX attributes + if (Node.isJsxOpeningElement(node) || Node.isJsxSelfClosingElement(node)) { + const route = getRouteForFile(abs); + const componentOrFn = getComponentOrFnName(node); + const tag = node.getTagNameNode().getText(); + const attrs = node.getAttributes().filter(Node.isJsxAttribute); + attrs.forEach((attr) => { + const name = getAttrName(attr); + const lower = name.toLowerCase(); + const value = getStringFromInitializer(attr); + if (!value) return; + let kind = null; + if (lower === 'placeholder') kind = 'placeholder'; + else if (lower === 'title') kind = 'title'; + else if (lower === 'alt') kind = 'alt'; + else if (lower.startsWith('aria-')) kind = 'aria'; + else if (lower === 'label') kind = 'label'; + if (kind) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind, + keyOrLocator: `${tag}.${name}`, + text: value, + line: getNodeLine(attr), + }); + } + }); + } + + // Interaction messages + if (Node.isCallExpression(node)) { + const route = getRouteForFile(abs); + const componentOrFn = getComponentOrFnName(node); + const expr = node.getExpression(); + let kind = null; + let keyOrLocator = ''; + if (Node.isPropertyAccessExpression(expr)) { + const left = expr.getExpression().getText(); + const name = expr.getName(); + if (left === 'toast' || left === 'message') { kind = 'toast'; keyOrLocator = `${left}.${name}`; } + if ((left || '').toLowerCase().includes('dialog')) { kind = 'dialog'; keyOrLocator = `${left}.${name}`; } + } else if (Node.isIdentifier(expr)) { + const id = expr.getText(); + if (id === 'alert' || id === 'confirm') { kind = 'dialog'; keyOrLocator = id; } + } + if (kind) { + const arg0 = node.getArguments()[0]; + if (arg0 && (Node.isStringLiteral(arg0) || Node.isNoSubstitutionTemplateLiteral(arg0))) { + const text = arg0.getLiteralText(); + pushItem(items, { route, file: path.relative(WORKDIR, abs), componentOrFn, kind, keyOrLocator, text, line: getNodeLine(node) }); + } + } + + // form.setError("field", { message: "..." }) + if (Node.isPropertyAccessExpression(expr) && expr.getName() === 'setError') { + const args = node.getArguments(); + if (args.length >= 2) { + const second = args[1]; + if (Node.isObjectLiteralExpression(second)) { + const msgProp = second.getProperty('message'); + if (msgProp && Node.isPropertyAssignment(msgProp)) { + const init = msgProp.getInitializer(); + if (init && (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) { + const text = init.getLiteralText(); + pushItem(items, { route, file: path.relative(WORKDIR, abs), componentOrFn, kind: 'error', keyOrLocator: 'form.setError', text, line: getNodeLine(msgProp) }); + } + } + } + } + } + + // Generic validation object { message: "..." } + const args = node.getArguments(); + for (const a of args) { + if (Node.isObjectLiteralExpression(a)) { + const prop = a.getProperty('message'); + if (prop && Node.isPropertyAssignment(prop)) { + const init = prop.getInitializer(); + if (init && (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) { + const text = init.getLiteralText(); + pushItem(items, { route, file: path.relative(WORKDIR, abs), componentOrFn, kind: 'validation', keyOrLocator: 'message', text, line: getNodeLine(prop) }); + } + } + } + } + } + }); +} + +function aggregate(items) { + const map = new Map(); + for (const it of items) { + const key = `${it.route}__${it.kind}__${it.keyOrLocator}__${it.text}`; + if (!map.has(key)) map.set(key, { item: it, count: 1 }); + else map.get(key).count += 1; + } + const result = []; + for (const { item, count } of map.values()) { + result.push({ ...item, count }); + } + return result; +} + +function toWorkbook(items) { + const rows = items.map((it) => ({ + route: it.route, + file: it.file, + componentOrFn: it.componentOrFn, + kind: it.kind, + keyOrLocator: it.keyOrLocator, + text: it.text, + line: it.line, + count: it.count || 1, + notes: it.notes || '', + })); + const ws = XLSX.utils.json_to_sheet(rows, { skipHeader: false }); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'copy'); + return wb; +} + +async function main() { + ensureExcelDir(); + const files = await collectFiles(); + const project = new Project({ tsConfigFilePath: path.join(WORKDIR, 'tsconfig.json'), skipAddingFilesFromTsConfig: true }); + const items = []; + for (const rel of files) { + const abs = path.join(WORKDIR, rel); + try { + extractFromSourceFile(abs, items, project); + } catch (e) { + // continue on parse errors + } + } + const aggregated = aggregate(items); + const wb = toWorkbook(aggregated); + const out = path.join(WORKDIR, 'docs', 'copy-audit.xlsx'); + XLSX.writeFile(wb, out); + console.log(`Wrote ${aggregated.length} rows to ${out}`); +} + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); + + diff --git a/scripts/extract-copy.ts b/scripts/extract-copy.ts new file mode 100644 index 0000000..58f8cd6 --- /dev/null +++ b/scripts/extract-copy.ts @@ -0,0 +1,367 @@ +/* + Extract all user-facing copy from the codebase into an Excel file. + Scans TS/TSX under src/, groups by Next.js App Router route, and writes docs/copy-audit.xlsx. +*/ + +import path from "node:path"; +import fs from "node:fs"; +import { globby } from "globby"; +import { Project, SyntaxKind, Node, JsxAttribute, StringLiteral, NoSubstitutionTemplateLiteral } from "ts-morph"; +import * as XLSX from "xlsx"; + +type CopyKind = + | "text" + | "placeholder" + | "title" + | "alt" + | "aria" + | "label" + | "toast" + | "dialog" + | "error" + | "validation"; + +interface CopyItem { + route: string; + file: string; + componentOrFn: string; + kind: CopyKind; + keyOrLocator: string; + text: string; + line: number; + notes?: string; +} + +const WORKDIR = process.cwd(); +const SRC_DIR = path.join(WORKDIR, "src"); +const APP_DIR = path.join(SRC_DIR, "app"); + +function ensureExcelDir() { + const docsDir = path.join(WORKDIR, "docs"); + if (!fs.existsSync(docsDir)) fs.mkdirSync(docsDir, { recursive: true }); +} + +function isMeaningfulText(value: string | undefined | null): value is string { + if (!value) return false; + const trimmed = value.replace(/\s+/g, " ").trim(); + if (!trimmed) return false; + // Filter obvious code-like tokens + if (/^[A-Za-z0-9_.$-]+$/.test(trimmed) && trimmed.length > 24) return false; + return true; +} + +function getRouteForFile(absFilePath: string): string { + if (!absFilePath.startsWith(APP_DIR)) return "shared"; + let dir = path.dirname(absFilePath); + // Walk up to find nearest folder that contains a page.tsx (or page.ts) + while (dir.startsWith(APP_DIR)) { + const pageTsx = path.join(dir, "page.tsx"); + const pageTs = path.join(dir, "page.ts"); + if (fs.existsSync(pageTsx) || fs.existsSync(pageTs)) { + const rel = path.relative(APP_DIR, dir); + return rel || "/"; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + // Fallback: route is the first app subfolder segment + const relToApp = path.relative(APP_DIR, absFilePath); + const parts = relToApp.split(path.sep); + return parts.length > 0 ? parts[0] : "shared"; +} + +function getComponentOrFnName(node: Node): string { + const fn = node.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration); + if (fn?.getName()) return fn.getName()!; + const varDecl = node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration); + if (varDecl?.getName()) return varDecl.getName(); + const cls = node.getFirstAncestorByKind(SyntaxKind.ClassDeclaration); + if (cls?.getName()) return cls.getName()!; + const sf = node.getSourceFile(); + return path.basename(sf.getFilePath()); +} + +function getNodeLine(node: Node): number { + const pos = node.getStartLineNumber(); + return pos ?? 1; +} + +function getAttrName(attr: JsxAttribute): string { + return attr.getNameNode().getText(); +} + +function getStringFromInitializer(attr: JsxAttribute): string | undefined { + const init = attr.getInitializer(); + if (!init) return undefined; + if (Node.isStringLiteral(init)) return init.getLiteralText(); + if (Node.isNoSubstitutionTemplateLiteral(init)) return init.getLiteralText(); + if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (!expr) return undefined; + if (Node.isStringLiteral(expr)) return expr.getLiteralText(); + if (Node.isNoSubstitutionTemplateLiteral(expr)) return expr.getLiteralText(); + } + return undefined; +} + +function pushItem(items: CopyItem[], item: CopyItem) { + if (!isMeaningfulText(item.text)) return; + items.push(item); +} + +async function collectFiles(): Promise { + const patterns = [ + "src/**/*.{ts,tsx}", + ]; + const ignore = [ + "**/node_modules/**", + "**/.next/**", + "**/__tests__/**", + "**/mocks/**", + "**/mock/**", + "**/*.d.ts", + ]; + return await globby(patterns, { gitignore: true, ignore }); +} + +function extractFromSourceFile(abs: string, items: CopyItem[], project: Project) { + const sf = project.addSourceFileAtPath(abs); + // JSX text nodes + sf.forEachDescendant((node) => { + if (Node.isJsxElement(node)) { + const opening = node.getOpeningElement(); + const componentOrFn = getComponentOrFnName(node); + const route = getRouteForFile(abs); + // 递归提取所有 JsxText 与 {'...'} 字面量 + const tagName = opening.getTagNameNode().getText(); + const textNodes = node.getDescendantsOfKind(SyntaxKind.JsxText); + textNodes.forEach((t) => { + const text = t.getText(); + const cleaned = text.replace(/\s+/g, " ").trim(); + if (isMeaningfulText(cleaned)) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "text", + keyOrLocator: tagName, + text: cleaned, + line: getNodeLine(t), + }); + } + }); + const exprs = node.getDescendantsOfKind(SyntaxKind.JsxExpression); + exprs.forEach((expr) => { + const inner = expr.getExpression(); + if (inner && (Node.isStringLiteral(inner) || Node.isNoSubstitutionTemplateLiteral(inner))) { + const cleaned = inner.getLiteralText().replace(/\s+/g, " ").trim(); + if (isMeaningfulText(cleaned)) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "text", + keyOrLocator: tagName, + text: cleaned, + line: getNodeLine(expr), + }); + } + } + }); + } + + // JSX attributes + if (Node.isJsxOpeningElement(node) || Node.isJsxSelfClosingElement(node)) { + const route = getRouteForFile(abs); + const componentOrFn = getComponentOrFnName(node); + const tag = Node.isJsxOpeningElement(node) + ? node.getTagNameNode().getText() + : node.getTagNameNode().getText(); + const attrs = node.getAttributes().filter(Node.isJsxAttribute); + attrs.forEach((attr) => { + const name = getAttrName(attr); + const lower = name.toLowerCase(); + const value = getStringFromInitializer(attr); + if (!value) return; + let kind: CopyKind | null = null; + if (lower === "placeholder") kind = "placeholder"; + else if (lower === "title") kind = "title"; + else if (lower === "alt") kind = "alt"; + else if (lower.startsWith("aria-")) kind = "aria"; + else if (lower === "label") kind = "label"; + if (kind) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind, + keyOrLocator: `${tag}.${name}`, + text: value, + line: getNodeLine(attr), + }); + } + }); + } + + // Interaction messages: toast.*, alert, confirm, message.* + if (Node.isCallExpression(node)) { + const route = getRouteForFile(abs); + const componentOrFn = getComponentOrFnName(node); + const expr = node.getExpression(); + let kind: CopyKind | null = null; + let keyOrLocator = ""; + if (Node.isPropertyAccessExpression(expr)) { + const left = expr.getExpression().getText(); + const name = expr.getName(); + if (left === "toast" || left === "message") { + kind = "toast"; + keyOrLocator = `${left}.${name}`; + } + if (left.toLowerCase().includes("dialog")) { + kind = "dialog"; + keyOrLocator = `${left}.${name}`; + } + } else if (Node.isIdentifier(expr)) { + const id = expr.getText(); + if (id === "alert" || id === "confirm") { + kind = "dialog"; + keyOrLocator = id; + } + } + if (kind) { + const arg0 = node.getArguments()[0]; + if (arg0 && (Node.isStringLiteral(arg0) || Node.isNoSubstitutionTemplateLiteral(arg0))) { + const text = (arg0 as StringLiteral | NoSubstitutionTemplateLiteral).getLiteralText(); + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind, + keyOrLocator, + text, + line: getNodeLine(node), + }); + } + } + + // form.setError("field", { message: "..." }) + if (Node.isPropertyAccessExpression(expr) && expr.getName() === "setError") { + const args = node.getArguments(); + if (args.length >= 2) { + const second = args[1]; + if (Node.isObjectLiteralExpression(second)) { + const msgProp = second.getProperty("message"); + if (msgProp && Node.isPropertyAssignment(msgProp)) { + const init = msgProp.getInitializer(); + if (init && (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) { + const text = init.getLiteralText(); + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "error", + keyOrLocator: "form.setError", + text, + line: getNodeLine(msgProp), + }); + } + } + } + } + } + + // Generic validation: any object literal { message: "..." } inside chained calls + const args = node.getArguments(); + for (const a of args) { + if (Node.isObjectLiteralExpression(a)) { + const prop = a.getProperty("message"); + if (prop && Node.isPropertyAssignment(prop)) { + const init = prop.getInitializer(); + if (init && (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) { + const text = init.getLiteralText(); + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "validation", + keyOrLocator: "message", + text, + line: getNodeLine(prop), + }); + } + } + } + } + } + }); +} + +function aggregate(items: CopyItem[]): CopyItem[] { + // Deduplicate by route+kind+text+keyOrLocator to keep first occurrence, count separately + const map = new Map(); + for (const it of items) { + const key = `${it.route}__${it.kind}__${it.keyOrLocator}__${it.text}`; + if (!map.has(key)) { + map.set(key, { item: it, count: 1 }); + } else { + map.get(key)!.count += 1; + } + } + const result: CopyItem[] = []; + for (const { item, count } of map.values()) { + (item as any).count = count; + result.push(item); + } + return result; +} + +function toWorkbook(items: CopyItem[]): XLSX.WorkBook { + const rows = items.map((it) => ({ + route: it.route, + file: it.file, + componentOrFn: it.componentOrFn, + kind: it.kind, + keyOrLocator: it.keyOrLocator, + text: it.text, + line: it.line, + count: (it as any).count ?? 1, + notes: it.notes ?? "", + })); + const ws = XLSX.utils.json_to_sheet(rows, { skipHeader: false }); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "copy"); + return wb; +} + +async function main() { + ensureExcelDir(); + const files = await collectFiles(); + const project = new Project({ + tsConfigFilePath: path.join(WORKDIR, "tsconfig.json"), + skipAddingFilesFromTsConfig: true, + }); + const items: CopyItem[] = []; + for (const rel of files) { + const abs = path.join(WORKDIR, rel); + try { + extractFromSourceFile(abs, items, project); + } catch (e) { + // swallow parse errors but continue + } + } + const aggregated = aggregate(items); + const wb = toWorkbook(aggregated); + const out = path.join(WORKDIR, "docs", "copy-audit.xlsx"); + XLSX.writeFile(wb, out); + // eslint-disable-next-line no-console + console.log(`Wrote ${aggregated.length} rows to ${out}`); +} + +main().catch((err) => { + // eslint-disable-next-line no-console + console.error(err); + process.exitCode = 1; +}); + + diff --git a/scripts/i18n-scan.ts b/scripts/i18n-scan.ts new file mode 100644 index 0000000..c358ffe --- /dev/null +++ b/scripts/i18n-scan.ts @@ -0,0 +1,430 @@ +/* + 使用 i18next-scanner 格式扫描项目中未翻译的文本 + 基于现有的 extract-copy.ts 工具,生成 i18next 格式的扫描报告 +*/ + +import path from "node:path"; +import fs from "node:fs"; +import { globby } from "globby"; +import { Project, SyntaxKind, Node, JsxAttribute, StringLiteral, NoSubstitutionTemplateLiteral } from "ts-morph"; +import * as XLSX from "xlsx"; + +type CopyKind = + | "text" + | "placeholder" + | "title" + | "alt" + | "aria" + | "label" + | "toast" + | "dialog" + | "error" + | "validation"; + +interface CopyItem { + route: string; + file: string; + componentOrFn: string; + kind: CopyKind; + keyOrLocator: string; + text: string; + line: number; + notes?: string; +} + +interface I18nKey { + key: string; + value: string; + context?: string; + file: string; + line: number; +} + +const WORKDIR = process.cwd(); +const SRC_DIR = path.join(WORKDIR, "src"); +const APP_DIR = path.join(SRC_DIR, "app"); + +function ensureExcelDir() { + const docsDir = path.join(WORKDIR, "docs"); + if (!fs.existsSync(docsDir)) fs.mkdirSync(docsDir, { recursive: true }); +} + +function isMeaningfulText(value: string | undefined | null): value is string { + if (!value) return false; + const trimmed = value.replace(/\s+/g, " ").trim(); + if (!trimmed) return false; + // Filter obvious code-like tokens + if (/^[A-Za-z0-9_.$-]+$/.test(trimmed) && trimmed.length > 24) return false; + return true; +} + +function getRouteForFile(absFilePath: string): string { + if (!absFilePath.startsWith(APP_DIR)) return "shared"; + let dir = path.dirname(absFilePath); + // Walk up to find nearest folder that contains a page.tsx (or page.ts) + while (dir.startsWith(APP_DIR)) { + const pageTsx = path.join(dir, "page.tsx"); + const pageTs = path.join(dir, "page.ts"); + if (fs.existsSync(pageTsx) || fs.existsSync(pageTs)) { + const rel = path.relative(APP_DIR, dir); + return rel || "/"; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + // Fallback: route is the first app subfolder segment + const relToApp = path.relative(APP_DIR, absFilePath); + const parts = relToApp.split(path.sep); + return parts.length > 0 ? parts[0] : "shared"; +} + +function getComponentOrFnName(node: Node): string { + const fn = node.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration); + if (fn?.getName()) return fn.getName()!; + const varDecl = node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration); + if (varDecl?.getName()) return varDecl.getName(); + const cls = node.getFirstAncestorByKind(SyntaxKind.ClassDeclaration); + if (cls?.getName()) return cls.getName()!; + const sf = node.getSourceFile(); + return path.basename(sf.getFilePath()); +} + +function getNodeLine(node: Node): number { + const pos = node.getStartLineNumber(); + return pos ?? 1; +} + +function getAttrName(attr: JsxAttribute): string { + return attr.getNameNode().getText(); +} + +function getStringFromInitializer(attr: JsxAttribute): string | undefined { + const init = attr.getInitializer(); + if (!init) return undefined; + if (Node.isStringLiteral(init)) return init.getLiteralText(); + if (Node.isNoSubstitutionTemplateLiteral(init)) return init.getLiteralText(); + if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (!expr) return undefined; + if (Node.isStringLiteral(expr)) return expr.getLiteralText(); + if (Node.isNoSubstitutionTemplateLiteral(expr)) return expr.getLiteralText(); + } + return undefined; +} + +function pushItem(items: CopyItem[], item: CopyItem) { + if (!isMeaningfulText(item.text)) return; + items.push(item); +} + +function generateI18nKey(item: CopyItem): string { + // 生成 i18next 格式的键名 + const route = item.route === "shared" ? "common" : item.route.replace(/[^a-zA-Z0-9]/g, "_"); + const component = item.componentOrFn.replace(/[^a-zA-Z0-9]/g, "_"); + const kind = item.kind; + const locator = item.keyOrLocator.replace(/[^a-zA-Z0-9]/g, "_"); + + return `${route}.${component}.${kind}.${locator}`.toLowerCase(); +} + +async function collectFiles(): Promise { + const patterns = [ + "src/**/*.{ts,tsx}", + ]; + const ignore = [ + "**/node_modules/**", + "**/.next/**", + "**/__tests__/**", + "**/mocks/**", + "**/mock/**", + "**/*.d.ts", + ]; + return await globby(patterns, { gitignore: true, ignore }); +} + +function extractFromSourceFile(abs: string, items: CopyItem[], project: Project) { + const sf = project.addSourceFileAtPath(abs); + // JSX text nodes + sf.forEachDescendant((node) => { + if (Node.isJsxElement(node)) { + const opening = node.getOpeningElement(); + const componentOrFn = getComponentOrFnName(node); + const route = getRouteForFile(abs); + // 递归提取所有 JsxText 与 {'...'} 字面量 + const tagName = opening.getTagNameNode().getText(); + const textNodes = node.getDescendantsOfKind(SyntaxKind.JsxText); + textNodes.forEach((t) => { + const text = t.getText(); + const cleaned = text.replace(/\s+/g, " ").trim(); + if (isMeaningfulText(cleaned)) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "text", + keyOrLocator: tagName, + text: cleaned, + line: getNodeLine(t), + }); + } + }); + const exprs = node.getDescendantsOfKind(SyntaxKind.JsxExpression); + exprs.forEach((expr) => { + const inner = expr.getExpression(); + if (inner && (Node.isStringLiteral(inner) || Node.isNoSubstitutionTemplateLiteral(inner))) { + const cleaned = inner.getLiteralText().replace(/\s+/g, " ").trim(); + if (isMeaningfulText(cleaned)) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "text", + keyOrLocator: tagName, + text: cleaned, + line: getNodeLine(expr), + }); + } + } + }); + } + + // JSX attributes + if (Node.isJsxOpeningElement(node) || Node.isJsxSelfClosingElement(node)) { + const route = getRouteForFile(abs); + const componentOrFn = getComponentOrFnName(node); + const tag = Node.isJsxOpeningElement(node) + ? node.getTagNameNode().getText() + : node.getTagNameNode().getText(); + const attrs = node.getAttributes().filter(Node.isJsxAttribute); + attrs.forEach((attr) => { + const name = getAttrName(attr); + const lower = name.toLowerCase(); + const value = getStringFromInitializer(attr); + if (!value) return; + let kind: CopyKind | null = null; + if (lower === "placeholder") kind = "placeholder"; + else if (lower === "title") kind = "title"; + else if (lower === "alt") kind = "alt"; + else if (lower.startsWith("aria-")) kind = "aria"; + else if (lower === "label") kind = "label"; + if (kind) { + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind, + keyOrLocator: `${tag}.${name}`, + text: value, + line: getNodeLine(attr), + }); + } + }); + } + + // Interaction messages: toast.*, alert, confirm, message.* + if (Node.isCallExpression(node)) { + const route = getRouteForFile(abs); + const componentOrFn = getComponentOrFnName(node); + const expr = node.getExpression(); + let kind: CopyKind | null = null; + let keyOrLocator = ""; + if (Node.isPropertyAccessExpression(expr)) { + const left = expr.getExpression().getText(); + const name = expr.getName(); + if (left === "toast" || left === "message") { + kind = "toast"; + keyOrLocator = `${left}.${name}`; + } + if (left.toLowerCase().includes("dialog")) { + kind = "dialog"; + keyOrLocator = `${left}.${name}`; + } + } else if (Node.isIdentifier(expr)) { + const id = expr.getText(); + if (id === "alert" || id === "confirm") { + kind = "dialog"; + keyOrLocator = id; + } + } + if (kind) { + const arg0 = node.getArguments()[0]; + if (arg0 && (Node.isStringLiteral(arg0) || Node.isNoSubstitutionTemplateLiteral(arg0))) { + const text = (arg0 as StringLiteral | NoSubstitutionTemplateLiteral).getLiteralText(); + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind, + keyOrLocator, + text, + line: getNodeLine(node), + }); + } + } + + // form.setError("field", { message: "..." }) + if (Node.isPropertyAccessExpression(expr) && expr.getName() === "setError") { + const args = node.getArguments(); + if (args.length >= 2) { + const second = args[1]; + if (Node.isObjectLiteralExpression(second)) { + const msgProp = second.getProperty("message"); + if (msgProp && Node.isPropertyAssignment(msgProp)) { + const init = msgProp.getInitializer(); + if (init && (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) { + const text = init.getLiteralText(); + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "error", + keyOrLocator: "form.setError", + text, + line: getNodeLine(msgProp), + }); + } + } + } + } + } + + // Generic validation: any object literal { message: "..." } inside chained calls + const args = node.getArguments(); + for (const a of args) { + if (Node.isObjectLiteralExpression(a)) { + const prop = a.getProperty("message"); + if (prop && Node.isPropertyAssignment(prop)) { + const init = prop.getInitializer(); + if (init && (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) { + const text = init.getLiteralText(); + pushItem(items, { + route, + file: path.relative(WORKDIR, abs), + componentOrFn, + kind: "validation", + keyOrLocator: "message", + text, + line: getNodeLine(prop), + }); + } + } + } + } + } + }); +} + +function aggregate(items: CopyItem[]): CopyItem[] { + // Deduplicate by route+kind+text+keyOrLocator to keep first occurrence, count separately + const map = new Map(); + for (const it of items) { + const key = `${it.route}__${it.kind}__${it.keyOrLocator}__${it.text}`; + if (!map.has(key)) { + map.set(key, { item: it, count: 1 }); + } else { + map.get(key)!.count += 1; + } + } + const result: CopyItem[] = []; + for (const { item, count } of map.values()) { + (item as any).count = count; + result.push(item); + } + return result; +} + +function generateI18nTranslation(items: CopyItem[]): Record { + const translation: Record = {}; + + items.forEach((item) => { + const key = generateI18nKey(item); + translation[key] = item.text; + }); + + return translation; +} + +function toWorkbook(items: CopyItem[]): XLSX.WorkBook { + const rows = items.map((it) => ({ + route: it.route, + file: it.file, + componentOrFn: it.componentOrFn, + kind: it.kind, + keyOrLocator: it.keyOrLocator, + text: it.text, + line: it.line, + count: (it as any).count ?? 1, + i18nKey: generateI18nKey(it), + notes: it.notes ?? "", + })); + const ws = XLSX.utils.json_to_sheet(rows, { skipHeader: false }); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "i18n-scan"); + return wb; +} + +async function main() { + ensureExcelDir(); + const files = await collectFiles(); + const project = new Project({ + tsConfigFilePath: path.join(WORKDIR, "tsconfig.json"), + skipAddingFilesFromTsConfig: true, + }); + const items: CopyItem[] = []; + for (const rel of files) { + const abs = path.join(WORKDIR, rel); + try { + extractFromSourceFile(abs, items, project); + } catch (e) { + // swallow parse errors but continue + } + } + const aggregated = aggregate(items); + + // 生成 i18next 格式的翻译文件 + const translation = generateI18nTranslation(aggregated); + const localesDir = path.join(WORKDIR, "public", "locales", "en"); + if (!fs.existsSync(localesDir)) { + fs.mkdirSync(localesDir, { recursive: true }); + } + const translationFile = path.join(localesDir, "translation.json"); + fs.writeFileSync(translationFile, JSON.stringify(translation, null, 2)); + + // 生成 Excel 报告 + const wb = toWorkbook(aggregated); + const out = path.join(WORKDIR, "docs", "i18n-scan-report.xlsx"); + XLSX.writeFile(wb, out); + + // 生成扫描报告 + const report = { + totalItems: aggregated.length, + uniqueTexts: new Set(aggregated.map(item => item.text)).size, + byRoute: aggregated.reduce((acc, item) => { + acc[item.route] = (acc[item.route] || 0) + 1; + return acc; + }, {} as Record), + byKind: aggregated.reduce((acc, item) => { + acc[item.kind] = (acc[item.kind] || 0) + 1; + return acc; + }, {} as Record), + translationKeys: Object.keys(translation).length + }; + + const reportFile = path.join(WORKDIR, "docs", "i18n-scan-report.json"); + fs.writeFileSync(reportFile, JSON.stringify(report, null, 2)); + + // eslint-disable-next-line no-console + console.log(`✅ i18next 扫描完成!`); + console.log(`📊 总扫描条目: ${aggregated.length}`); + console.log(`🔑 生成翻译键: ${Object.keys(translation).length}`); + console.log(`📁 翻译文件: ${translationFile}`); + console.log(`📋 Excel 报告: ${out}`); + console.log(`📄 JSON 报告: ${reportFile}`); +} + +main().catch((err) => { + // eslint-disable-next-line no-console + console.error(err); + process.exitCode = 1; +}); diff --git a/scripts/reset-and-apply-translations.cjs b/scripts/reset-and-apply-translations.cjs new file mode 100644 index 0000000..25c57de --- /dev/null +++ b/scripts/reset-and-apply-translations.cjs @@ -0,0 +1,367 @@ +/* + CommonJS runtime for resetting files and applying translations from Excel to source code. + This script first resets files to their original state, then applies translations. +*/ +const path = require('node:path'); +const fs = require('node:fs'); +const { execSync } = require('child_process'); +const { Project, SyntaxKind, Node } = require('ts-morph'); +const XLSX = require('xlsx'); + +const WORKDIR = process.cwd(); +const TRANSLATES_FILE = path.join(WORKDIR, 'scripts', 'translates.xlsx'); +const REPORT_FILE = path.join(WORKDIR, 'scripts', 'translation-report.json'); +const CONFLICTS_FILE = path.join(WORKDIR, 'scripts', 'translation-conflicts.xlsx'); + +// 统计信息 +const stats = { + total: 0, + success: 0, + conflicts: 0, + fileNotFound: 0, + textNotFound: 0, + multipleMatches: 0 +}; + +// 冲突列表 +const conflicts = []; + +// 成功替换列表 +const successfulReplacements = []; + +function resetFiles() { + console.log('🔄 重置文件到原始状态...'); + try { + // 使用 git 重置所有修改的文件 + execSync('git checkout -- .', { cwd: WORKDIR, stdio: 'inherit' }); + console.log('✅ 文件重置完成'); + } catch (error) { + console.error('❌ 重置文件失败:', error.message); + process.exit(1); + } +} + +function loadTranslations() { + console.log('📖 读取翻译数据...'); + const wb = XLSX.readFile(TRANSLATES_FILE); + const ws = wb.Sheets[wb.SheetNames[0]]; + const data = XLSX.utils.sheet_to_json(ws, { defval: '' }); + + // 筛选出需要替换的条目 + let translations = data.filter(row => + row.text && + row.corrected_text && + row.text !== row.corrected_text + ); + + // 去重:按 file + line + text 去重,保留第一个 + const seen = new Set(); + translations = translations.filter(row => { + const key = `${row.file}:${row.line}:${row.text}`; + if (seen.has(key)) { + return false; + } + seen.add(key); + return true; + }); + + console.log(`📊 找到 ${translations.length} 条需要替换的翻译(已去重)`); + stats.total = translations.length; + + return translations; +} + +function groupByFile(translations) { + const groups = new Map(); + for (const translation of translations) { + const filePath = path.join(WORKDIR, translation.file); + if (!groups.has(filePath)) { + groups.set(filePath, []); + } + groups.get(filePath).push(translation); + } + return groups; +} + +function findTextInFile(sourceFile, translation) { + const { text, kind } = translation; + const matches = []; + + sourceFile.forEachDescendant((node) => { + // 根据 kind 类型进行不同的匹配 + if (kind === 'text') { + // 查找 JSX 文本节点 + if (Node.isJsxText(node)) { + const nodeText = node.getText().replace(/\s+/g, ' ').trim(); + if (nodeText === text) { + matches.push({ node, type: 'jsx-text', line: node.getStartLineNumber() }); + } + } + // 查找 JSX 表达式中的字符串 + if (Node.isJsxExpression(node)) { + const expr = node.getExpression(); + if (expr && (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr))) { + const nodeText = expr.getLiteralText(); + if (nodeText === text) { + matches.push({ node: expr, type: 'jsx-expression', line: node.getStartLineNumber() }); + } + } + } + } else if (['placeholder', 'title', 'alt', 'label', 'aria'].includes(kind)) { + // 查找 JSX 属性 + if (Node.isJsxAttribute(node)) { + const name = node.getNameNode().getText().toLowerCase(); + const value = getStringFromInitializer(node); + if (value === text) { + matches.push({ node, type: 'jsx-attribute', line: node.getStartLineNumber() }); + } + } + } else if (['toast', 'dialog', 'error', 'validation'].includes(kind)) { + // 查找函数调用中的字符串参数 + if (Node.isCallExpression(node)) { + const args = node.getArguments(); + for (const arg of args) { + if (Node.isStringLiteral(arg) || Node.isNoSubstitutionTemplateLiteral(arg)) { + const nodeText = arg.getLiteralText(); + if (nodeText === text) { + matches.push({ node: arg, type: 'function-arg', line: node.getStartLineNumber() }); + } + } + } + } + } + }); + + return matches; +} + +function getStringFromInitializer(attr) { + const init = attr.getInitializer(); + if (!init) return undefined; + if (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init)) { + return init.getLiteralText(); + } + if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (!expr) return undefined; + if (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr)) { + return expr.getLiteralText(); + } + } + return undefined; +} + +function replaceText(node, newText, type) { + try { + if (type === 'jsx-text') { + // JSX 文本节点需要特殊处理,保持空白字符 + const originalText = node.getText(); + const newTextWithWhitespace = originalText.replace(/\S+/g, newText); + node.replaceWithText(newTextWithWhitespace); + } else if (type === 'jsx-expression' || type === 'function-arg') { + // 字符串字面量 + if (Node.isStringLiteral(node)) { + node.replaceWithText(`"${newText}"`); + } else if (Node.isNoSubstitutionTemplateLiteral(node)) { + node.replaceWithText(`\`${newText}\``); + } + } else if (type === 'jsx-attribute') { + // JSX 属性值 + const init = node.getInitializer(); + if (init) { + if (Node.isStringLiteral(init)) { + init.replaceWithText(`"${newText}"`); + } else if (Node.isNoSubstitutionTemplateLiteral(init)) { + init.replaceWithText(`\`${newText}\``); + } else if (Node.isJsxExpression(init)) { + const expr = init.getExpression(); + if (expr && (Node.isStringLiteral(expr) || Node.isNoSubstitutionTemplateLiteral(expr))) { + if (Node.isStringLiteral(expr)) { + expr.replaceWithText(`"${newText}"`); + } else { + expr.replaceWithText(`\`${newText}\``); + } + } + } + } + } + return true; + } catch (error) { + console.error(`❌ 替换失败: ${error.message}`); + return false; + } +} + +function processFile(filePath, translations) { + if (!fs.existsSync(filePath)) { + console.log(`❌ 文件不存在: ${path.relative(WORKDIR, filePath)}`); + translations.forEach(t => { + conflicts.push({ + ...t, + conflictType: 'FILE_NOT_FOUND', + conflictReason: '文件不存在' + }); + }); + stats.fileNotFound += translations.length; + return; + } + + console.log(`📝 处理文件: ${path.relative(WORKDIR, filePath)}`); + + try { + const project = new Project({ + tsConfigFilePath: path.join(WORKDIR, 'tsconfig.json'), + skipAddingFilesFromTsConfig: true + }); + const sourceFile = project.addSourceFileAtPath(filePath); + + for (const translation of translations) { + const { text, corrected_text, line, kind } = translation; + + // 在文件中查找匹配的文本 + const matches = findTextInFile(sourceFile, translation); + + if (matches.length === 0) { + conflicts.push({ + ...translation, + conflictType: 'TEXT_NOT_FOUND_IN_FILE', + conflictReason: '在文件中找不到匹配的文本' + }); + stats.textNotFound++; + continue; + } + + if (matches.length > 1) { + conflicts.push({ + ...translation, + conflictType: 'MULTIPLE_MATCHES', + conflictReason: `找到 ${matches.length} 个匹配项,需要人工确认` + }); + stats.multipleMatches++; + continue; + } + + // 执行替换 + const match = matches[0]; + const success = replaceText(match.node, corrected_text, match.type); + + if (success) { + successfulReplacements.push({ + ...translation, + actualLine: match.line, + replacementType: match.type + }); + stats.success++; + console.log(`✅ 替换成功: "${text}" -> "${corrected_text}" (行 ${match.line})`); + } else { + conflicts.push({ + ...translation, + conflictType: 'REPLACEMENT_FAILED', + conflictReason: '替换操作失败' + }); + stats.conflicts++; + } + } + + // 保存修改后的文件 + sourceFile.saveSync(); + + } catch (error) { + console.error(`❌ 处理文件失败: ${filePath}`, error.message); + translations.forEach(t => { + conflicts.push({ + ...t, + conflictType: 'PARSE_ERROR', + conflictReason: `文件解析失败: ${error.message}` + }); + }); + stats.conflicts += translations.length; + } +} + +function generateReport() { + console.log('\n📊 生成报告...'); + + // 生成成功替换报告 + const report = { + timestamp: new Date().toISOString(), + stats, + successfulReplacements, + conflicts: conflicts.map(c => ({ + file: c.file, + line: c.line, + text: c.text, + corrected_text: c.corrected_text, + conflictType: c.conflictType, + conflictReason: c.conflictReason + })) + }; + + fs.writeFileSync(REPORT_FILE, JSON.stringify(report, null, 2)); + console.log(`📄 成功替换报告已保存: ${REPORT_FILE}`); + + // 生成冲突报告 Excel + if (conflicts.length > 0) { + const conflictRows = conflicts.map(c => ({ + file: c.file, + line: c.line, + text: c.text, + corrected_text: c.corrected_text, + conflictType: c.conflictType, + conflictReason: c.conflictReason, + route: c.route, + componentOrFn: c.componentOrFn, + kind: c.kind, + keyOrLocator: c.keyOrLocator + })); + + const ws = XLSX.utils.json_to_sheet(conflictRows); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, 'conflicts'); + XLSX.writeFile(wb, CONFLICTS_FILE); + console.log(`📄 冲突报告已保存: ${CONFLICTS_FILE}`); + } +} + +function printSummary() { + console.log('\n📈 处理完成!'); + console.log(`总翻译条目: ${stats.total}`); + console.log(`✅ 成功替换: ${stats.success}`); + console.log(`❌ 文件不存在: ${stats.fileNotFound}`); + console.log(`❌ 文本未找到: ${stats.textNotFound}`); + console.log(`❌ 多处匹配: ${stats.multipleMatches}`); + console.log(`❌ 其他冲突: ${stats.conflicts}`); + console.log(`\n成功率: ${((stats.success / stats.total) * 100).toFixed(1)}%`); +} + +async function main() { + console.log('🚀 开始重置并应用翻译...\n'); + + try { + // 1. 重置文件到原始状态 + resetFiles(); + + // 2. 读取翻译数据 + const translations = loadTranslations(); + + // 3. 按文件分组 + const fileGroups = groupByFile(translations); + + // 4. 处理每个文件 + for (const [filePath, fileTranslations] of fileGroups) { + processFile(filePath, fileTranslations); + } + + // 5. 生成报告 + generateReport(); + + // 6. 打印总结 + printSummary(); + + } catch (error) { + console.error('❌ 执行失败:', error); + process.exitCode = 1; + } +} + +main(); diff --git a/scripts/translates.xlsx b/scripts/translates.xlsx new file mode 100644 index 0000000..8722579 Binary files /dev/null and b/scripts/translates.xlsx differ diff --git a/scripts/translation-conflicts.xlsx b/scripts/translation-conflicts.xlsx new file mode 100644 index 0000000..885cb1c Binary files /dev/null and b/scripts/translation-conflicts.xlsx differ diff --git a/scripts/translation-report.json b/scripts/translation-report.json new file mode 100644 index 0000000..c6ca5f9 --- /dev/null +++ b/scripts/translation-report.json @@ -0,0 +1,5043 @@ +{ + "timestamp": "2025-10-19T13:02:25.634Z", + "stats": { + "total": 378, + "success": 334, + "conflicts": 0, + "fileNotFound": 0, + "textNotFound": 24, + "multipleMatches": 20 + }, + "successfulReplacements": [ + { + "route": "shared", + "file": "src/components/mock-provider.tsx", + "componentOrFn": "MockProvider", + "kind": "text", + "keyOrLocator": "div", + "text": "正在初始化开发环境...", + "line": 61, + "count": 2, + "notes": "", + "corrected_text": "Initializing development environment...", + "actualLine": 61, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/hooks/auth.ts", + "componentOrFn": "useLogout", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "已退出登录", + "line": 45, + "count": 1, + "notes": "", + "corrected_text": "Logged out", + "actualLine": 45, + "replacementType": "function-arg" + }, + { + "route": "shared", + "file": "src/hooks/auth.ts", + "componentOrFn": "useRegister", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "注册成功!", + "line": 129, + "count": 1, + "notes": "", + "corrected_text": "Successful registration!", + "actualLine": 129, + "replacementType": "function-arg" + }, + { + "route": "shared", + "file": "src/hooks/auth.ts", + "componentOrFn": "useRegister", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "注册失败", + "line": 140, + "count": 1, + "notes": "", + "corrected_text": "Registration failed.", + "actualLine": 140, + "replacementType": "function-arg" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试选项", + "line": 90, + "count": 3, + "notes": "", + "corrected_text": "test options", + "actualLine": 90, + "replacementType": "jsx-text" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试直接 API 请求", + "line": 97, + "count": 3, + "notes": "", + "corrected_text": "Testing direct API requests", + "actualLine": 97, + "replacementType": "jsx-text" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试 Mock API 请求", + "line": 101, + "count": 3, + "notes": "", + "corrected_text": "Testing Mock API Requests", + "actualLine": 101, + "replacementType": "jsx-text" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "清除结果", + "line": 105, + "count": 3, + "notes": "", + "corrected_text": "Clear result", + "actualLine": 105, + "replacementType": "jsx-text" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "环境信息", + "line": 111, + "count": 3, + "notes": "", + "corrected_text": "Environmental information", + "actualLine": 111, + "replacementType": "jsx-text" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试结果", + "line": 131, + "count": 2, + "notes": "", + "corrected_text": "Test Results", + "actualLine": 131, + "replacementType": "jsx-text" + }, + { + "route": "debug-mock", + "file": "src/app/debug-mock/page.tsx", + "componentOrFn": "DebugMockPage", + "kind": "text", + "keyOrLocator": "div", + "text": "等待测试结果...", + "line": 134, + "count": 4, + "notes": "", + "corrected_text": "Waiting for the test results...", + "actualLine": 134, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "组件演示", + "line": 61, + "count": 3, + "notes": "", + "corrected_text": "Component demo", + "actualLine": 61, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "可折叠侧边栏", + "line": 65, + "count": 3, + "notes": "", + "corrected_text": "Collapsible Sidebar", + "actualLine": 65, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "左侧侧边栏支持以下功能:", + "line": 68, + "count": 4, + "notes": "", + "corrected_text": "The left sidebar supports the following functions:", + "actualLine": 68, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "点击顶部折叠/展开按钮切换侧边栏宽度", + "line": 71, + "count": 4, + "notes": "", + "corrected_text": "Click the top fold/expand button to toggle the sidebar width", + "actualLine": 71, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "导航菜单支持选中状态和图标切换", + "line": 72, + "count": 4, + "notes": "", + "corrected_text": "Navigation menu supports selection status and icon switching", + "actualLine": 72, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "聊天列表显示用户头像、消息预览、时间和未读数量", + "line": 73, + "count": 4, + "notes": "", + "corrected_text": "Chat list shows user avatar, message preview, time, and unread count", + "actualLine": 73, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "支持用户标签和温度显示", + "line": 74, + "count": 4, + "notes": "", + "corrected_text": "Support user label and temperature display", + "actualLine": 74, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "底部通知功能带有数量徽章", + "line": 75, + "count": 4, + "notes": "", + "corrected_text": "The bottom notification function comes with a quantity badge.", + "actualLine": 75, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "流畅的动画过渡效果", + "line": 76, + "count": 4, + "notes": "", + "corrected_text": "Smooth animation transitions", + "actualLine": 76, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "Alert Dialog 对话框", + "line": 83, + "count": 3, + "notes": "", + "corrected_text": "Alert Dialog dialog box", + "actualLine": 83, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "删除警告", + "line": 88, + "count": 4, + "notes": "", + "corrected_text": "Remove warning", + "actualLine": 88, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "AI 生成确认", + "line": 107, + "count": 4, + "notes": "", + "corrected_text": "AI generated confirmation", + "actualLine": 107, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "Loading 对话框", + "line": 128, + "count": 4, + "notes": "", + "corrected_text": "Loading dialog box", + "actualLine": 128, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "确认操作", + "line": 132, + "count": 4, + "notes": "", + "corrected_text": "confirm operation", + "actualLine": 132, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "此操作将永久删除您的数据,确定要继续吗?", + "line": 134, + "count": 4, + "notes": "", + "corrected_text": "This operation will permanently delete your data. Are you sure you want to continue?", + "actualLine": 134, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "无关闭按钮", + "line": 155, + "count": 4, + "notes": "", + "corrected_text": "No close button", + "actualLine": 155, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "重要提示", + "line": 159, + "count": 4, + "notes": "", + "corrected_text": "Important Note", + "actualLine": 159, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "这是一个重要提示,您必须选择一个选项才能继续。", + "line": 161, + "count": 4, + "notes": "", + "corrected_text": "This is an important reminder that you must choose an option to proceed.", + "actualLine": 161, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "我知道了", + "line": 165, + "count": 4, + "notes": "", + "corrected_text": "Oh, I see.", + "actualLine": 165, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "继续操作", + "line": 166, + "count": 4, + "notes": "", + "corrected_text": "Continue operation", + "actualLine": 166, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "普通按钮 Loading 效果", + "line": 175, + "count": 3, + "notes": "", + "corrected_text": "Normal Button Loading Effect", + "actualLine": 175, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "小按钮", + "line": 207, + "count": 4, + "notes": "", + "corrected_text": "Small button", + "actualLine": 207, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "图标按钮 Loading 效果", + "line": 214, + "count": 3, + "notes": "", + "corrected_text": "Icon button Loading effect", + "actualLine": 214, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "块级按钮 Loading 效果", + "line": 247, + "count": 3, + "notes": "", + "corrected_text": "Block Button Loading Effect", + "actualLine": 247, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "手动控制 Loading 状态", + "line": 260, + "count": 3, + "notes": "", + "corrected_text": "Manually Control Loading Status", + "actualLine": 260, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "切换 Loading 1", + "line": 266, + "count": 4, + "notes": "", + "corrected_text": "Switch Loading 1", + "actualLine": 266, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "切换 Loading 2", + "line": 272, + "count": 4, + "notes": "", + "corrected_text": "Toggle Loading 2", + "actualLine": 272, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "切换 Loading 3", + "line": 278, + "count": 4, + "notes": "", + "corrected_text": "Toggle Loading 3", + "actualLine": 278, + "replacementType": "jsx-text" + }, + { + "route": "demo", + "file": "src/app/demo/page.tsx", + "componentOrFn": "DemoPage", + "kind": "text", + "keyOrLocator": "div", + "text": "切换 Loading 4", + "line": 284, + "count": 4, + "notes": "", + "corrected_text": "Toggle Loading 4", + "actualLine": 284, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试服务端渲染时的设备ID生成和管理", + "line": 18, + "count": 2, + "notes": "", + "corrected_text": "Device ID generation and management when testing server-side rendering", + "actualLine": 18, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "当前设备ID (Cookie)", + "line": 29, + "count": 3, + "notes": "", + "corrected_text": "Current Device ID (Cookie)", + "actualLine": 29, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "中间件传递的设备ID (Header)", + "line": 38, + "count": 3, + "notes": "", + "corrected_text": "Device ID passed by middleware (Header)", + "actualLine": 38, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "状态说明:", + "line": 47, + "count": 3, + "notes": "", + "corrected_text": "Status description:", + "actualLine": 47, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 首次访问: 中间件生成设备ID并设置cookie", + "line": 49, + "count": 3, + "notes": "", + "corrected_text": "• First visit: Middleware generates device ID and sets cookies", + "actualLine": 49, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 后续访问: 从cookie读取现有设备ID", + "line": 50, + "count": 3, + "notes": "", + "corrected_text": "• Subsequent visits: Read the existing device ID from the cookie", + "actualLine": 50, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 设备ID通过header传递给服务端组件", + "line": 51, + "count": 3, + "notes": "", + "corrected_text": "• Device ID is passed to server level component via header", + "actualLine": 51, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "渲染时间", + "line": 71, + "count": 3, + "notes": "", + "corrected_text": "render time", + "actualLine": 71, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "渲染环境", + "line": 78, + "count": 3, + "notes": "", + "corrected_text": "rendering environment", + "actualLine": 78, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "服务端渲染 (SSR)", + "line": 80, + "count": 4, + "notes": "", + "corrected_text": "Server-side rendering (SSR)", + "actualLine": 80, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "1. 中间件处理 (middleware.ts)", + "line": 95, + "count": 3, + "notes": "", + "corrected_text": "1. Middleware processing (middleware.ts)", + "actualLine": 95, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 检查请求中是否已有设备ID cookie", + "line": 97, + "count": 3, + "notes": "", + "corrected_text": "• Check if there is a device ID cookie in the request", + "actualLine": 97, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 如果没有,使用 User-Agent 生成新的设备ID", + "line": 98, + "count": 3, + "notes": "", + "corrected_text": "• If not, use User-Agent to generate a new device ID", + "actualLine": 98, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 将设备ID设置为响应cookie", + "line": 99, + "count": 3, + "notes": "", + "corrected_text": "• Set the device ID to respond to cookies", + "actualLine": 99, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 通过 x-device-id header 传递给服务端组件", + "line": 100, + "count": 3, + "notes": "", + "corrected_text": "Pass to server level component via x-device-id header", + "actualLine": 100, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 在根布局中检查设备ID状态", + "line": 107, + "count": 3, + "notes": "", + "corrected_text": "• Check device ID status in root layout", + "actualLine": 107, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 从cookie和header两个来源获取设备ID", + "line": 108, + "count": 3, + "notes": "", + "corrected_text": "Get the device ID from both cookies and headers", + "actualLine": 108, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 记录设备ID状态用于调试", + "line": 109, + "count": 3, + "notes": "", + "corrected_text": "• Record device ID status for debugging", + "actualLine": 109, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "3. 服务端API调用", + "line": 114, + "count": 3, + "notes": "", + "corrected_text": "3. API calls at the server level", + "actualLine": 114, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 所有服务端API请求都会尝试携带设备ID", + "line": 116, + "count": 3, + "notes": "", + "corrected_text": "All server level API requests attempt to carry the device ID", + "actualLine": 116, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 通过 AUTH_DID 请求头发送", + "line": 117, + "count": 3, + "notes": "", + "corrected_text": "• Send via AUTH_DID request header", + "actualLine": 117, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 支持服务端渲染的预请求功能", + "line": 118, + "count": 3, + "notes": "", + "corrected_text": "• Pre-request function that supports server-side rendering", + "actualLine": 118, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "4. 限制说明", + "line": 123, + "count": 3, + "notes": "", + "corrected_text": "4. Restrictions", + "actualLine": 123, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• App Router 中只能在中间件或 Route Handler 中修改 cookies", + "line": 125, + "count": 3, + "notes": "", + "corrected_text": "• App Router can only modify cookies in middleware or Route Handler", + "actualLine": 125, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 服务端组件只能读取cookies,不能修改", + "line": 126, + "count": 3, + "notes": "", + "corrected_text": "• Server level components can only read cookies and cannot be modified", + "actualLine": 126, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 首次访问时设备ID可能在后续请求中才可用", + "line": 127, + "count": 3, + "notes": "", + "corrected_text": "The device ID may not be available on subsequent requests until the first visit", + "actualLine": 127, + "replacementType": "jsx-text" + }, + { + "route": "server-device-test", + "file": "src/app/server-device-test/page.tsx", + "componentOrFn": "ServerDeviceTestPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 这是 Next.js 13+ 的架构限制", + "line": 128, + "count": 3, + "notes": "", + "corrected_text": "This is an architectural limitation of Next.js 13 +", + "actualLine": 128, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "头像裁剪组件测试", + "line": 68, + "count": 3, + "notes": "", + "corrected_text": "Avatar Crop Component Test", + "actualLine": 68, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "基于设计稿还原的头像裁剪弹窗", + "line": 71, + "count": 3, + "notes": "", + "corrected_text": "Avatar clipping pop-up window based on design draft restoration", + "actualLine": 71, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "重置", + "line": 94, + "count": 3, + "notes": "", + "corrected_text": "reset", + "actualLine": 94, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "已选择:", + "line": 99, + "count": 3, + "notes": "", + "corrected_text": "Selected:", + "actualLine": 99, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "裁剪头像", + "line": 108, + "count": 2, + "notes": "", + "corrected_text": "crop avatar", + "actualLine": 108, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "打开头像裁剪器", + "line": 115, + "count": 3, + "notes": "", + "corrected_text": "Open avatar clipper", + "actualLine": 115, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "点击按钮打开裁剪弹窗,调整图片位置和大小", + "line": 118, + "count": 4, + "notes": "", + "corrected_text": "Click the button to open the cropping pop-up window and adjust the position and size of the picture.", + "actualLine": 118, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "原图", + "line": 129, + "count": 3, + "notes": "", + "corrected_text": "original image", + "actualLine": 129, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "裁剪后的头像", + "line": 144, + "count": 3, + "notes": "", + "corrected_text": "Cropped avatar", + "actualLine": 144, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "下载头像", + "line": 160, + "count": 5, + "notes": "", + "corrected_text": "Download avatar", + "actualLine": 160, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "功能说明", + "line": 170, + "count": 2, + "notes": "", + "corrected_text": "Function Description", + "actualLine": 170, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 支持拖拽调整图片位置", + "line": 172, + "count": 4, + "notes": "", + "corrected_text": "• Support drag and drop to adjust the position of the picture", + "actualLine": 172, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 使用滑块或 +/- 按钮调整缩放", + "line": 173, + "count": 4, + "notes": "", + "corrected_text": "• Use sliders or +/- buttons to adjust zoom", + "actualLine": 173, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 自动生成圆形头像", + "line": 174, + "count": 4, + "notes": "", + "corrected_text": "• Automatically generate round avatars", + "actualLine": 174, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 点击背景或 X 按钮关闭弹窗", + "line": 175, + "count": 4, + "notes": "", + "corrected_text": "• Click the background or X button to close the pop-up window", + "actualLine": 175, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• Cancel 取消操作,Confirm 确认裁剪", + "line": 176, + "count": 4, + "notes": "", + "corrected_text": "• Cancel Cancel operation, Confirm crop", + "actualLine": 176, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "设计还原", + "line": 182, + "count": 2, + "notes": "", + "corrected_text": "Design Restore", + "actualLine": 182, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 已实现的设计元素", + "line": 185, + "count": 4, + "notes": "", + "corrected_text": "✅ Implemented design elements", + "actualLine": 185, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 深色半透明背景遮罩", + "line": 187, + "count": 6, + "notes": "", + "corrected_text": "• Dark translucent background mask", + "actualLine": 187, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 圆形裁剪区域高亮显示", + "line": 188, + "count": 6, + "notes": "", + "corrected_text": "• Highlight the circular cropping area", + "actualLine": 188, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 底部缩放控制滑块", + "line": 189, + "count": 6, + "notes": "", + "corrected_text": "• Bottom zoom control slider", + "actualLine": 189, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• +/- 缩放按钮", + "line": 190, + "count": 6, + "notes": "", + "corrected_text": "• +/- zoom button", + "actualLine": 190, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 左上角关闭按钮", + "line": 192, + "count": 6, + "notes": "", + "corrected_text": "• Close button in the upper left corner", + "actualLine": 192, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 渐变色 Confirm 按钮", + "line": 193, + "count": 6, + "notes": "", + "corrected_text": "• Confirm button for gradual color change", + "actualLine": 193, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 按钮采用毛玻璃效果", + "line": 199, + "count": 6, + "notes": "", + "corrected_text": "• The button adopts frosted glass effect", + "actualLine": 199, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 滑块使用白色圆形按钮", + "line": 200, + "count": 6, + "notes": "", + "corrected_text": "• Slider uses white round buttons", + "actualLine": 200, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 完全覆盖屏幕的全屏弹窗", + "line": 201, + "count": 6, + "notes": "", + "corrected_text": "• A full-screen pop-up window that completely covers the screen", + "actualLine": 201, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 响应式布局适配移动端", + "line": 202, + "count": 6, + "notes": "", + "corrected_text": "• Responsive layout for mobile end", + "actualLine": 202, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "• 平滑的交互动画效果", + "line": 203, + "count": 6, + "notes": "", + "corrected_text": "• Smooth interactive animation effects", + "actualLine": 203, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "原图", + "line": 133, + "count": 1, + "notes": "", + "corrected_text": "original image", + "actualLine": 133, + "replacementType": "jsx-attribute" + }, + { + "route": "test-avatar-crop", + "file": "src/app/test-avatar-crop/page.tsx", + "componentOrFn": "TestAvatarCropPage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "裁剪后的头像", + "line": 149, + "count": 1, + "notes": "", + "corrected_text": "Cropped avatar", + "actualLine": 149, + "replacementType": "jsx-attribute" + }, + { + "route": "test-avatar-setting", + "file": "src/app/test-avatar-setting/page.tsx", + "componentOrFn": "TestAvatarSettingPage", + "kind": "text", + "keyOrLocator": "div", + "text": "头像设置测试", + "line": 33, + "count": 2, + "notes": "", + "corrected_text": "avatar setup test", + "actualLine": 33, + "replacementType": "jsx-text" + }, + { + "route": "test-avatar-setting", + "file": "src/app/test-avatar-setting/page.tsx", + "componentOrFn": "TestAvatarSettingPage", + "kind": "text", + "keyOrLocator": "div", + "text": "打开头像设置", + "line": 55, + "count": 1, + "notes": "", + "corrected_text": "Open avatar settings", + "actualLine": 55, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "Discord登录失败", + "line": 38, + "count": 1, + "notes": "", + "corrected_text": "Discord login failed", + "actualLine": 38, + "replacementType": "function-arg" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "Mock登录失败", + "line": 83, + "count": 1, + "notes": "", + "corrected_text": "Mock login failed", + "actualLine": 83, + "replacementType": "function-arg" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "toast", + "keyOrLocator": "toast.warning", + "text": "配置缺失", + "line": 109, + "count": 1, + "notes": "", + "corrected_text": "Missing configuration", + "actualLine": 109, + "replacementType": "function-arg" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "登录成功", + "line": 116, + "count": 1, + "notes": "", + "corrected_text": "Login successful", + "actualLine": 116, + "replacementType": "function-arg" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "退出成功", + "line": 126, + "count": 1, + "notes": "", + "corrected_text": "Exit successfully", + "actualLine": 126, + "replacementType": "function-arg" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "Discord登录测试页面", + "line": 136, + "count": 2, + "notes": "", + "corrected_text": "Discord login test page", + "actualLine": 136, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试Discord OAuth登录功能和Mock接口", + "line": 137, + "count": 2, + "notes": "", + "corrected_text": "Testing Discord OAuth Login Function and Mock Interface", + "actualLine": 137, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "用户状态", + "line": 143, + "count": 1, + "notes": "", + "corrected_text": "user status", + "actualLine": 143, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "加载中...", + "line": 147, + "count": 1, + "notes": "", + "corrected_text": "Loading...", + "actualLine": 147, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "用户ID:", + "line": 150, + "count": 2, + "notes": "", + "corrected_text": "User ID:", + "actualLine": 150, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "昵称:", + "line": 151, + "count": 2, + "notes": "", + "corrected_text": "Nickname:", + "actualLine": 151, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "头像:", + "line": 152, + "count": 2, + "notes": "", + "corrected_text": "Avatar:", + "actualLine": 152, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "性别:", + "line": 153, + "count": 2, + "notes": "", + "corrected_text": "Gender:", + "actualLine": 153, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "生日:", + "line": 154, + "count": 2, + "notes": "", + "corrected_text": "Birthday:", + "actualLine": 154, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "需要完善信息:", + "line": 155, + "count": 2, + "notes": "", + "corrected_text": "Need to improve information:", + "actualLine": 155, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "未登录", + "line": 158, + "count": 1, + "notes": "", + "corrected_text": "Not logged in", + "actualLine": 158, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试操作", + "line": 166, + "count": 1, + "notes": "", + "corrected_text": "test operation", + "actualLine": 166, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "操作日志", + "line": 191, + "count": 1, + "notes": "", + "corrected_text": "operation log", + "actualLine": 191, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "清空日志", + "line": 193, + "count": 1, + "notes": "", + "corrected_text": "clear log", + "actualLine": 193, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "暂无日志", + "line": 199, + "count": 2, + "notes": "", + "corrected_text": "No log yet", + "actualLine": 199, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "配置说明", + "line": 216, + "count": 1, + "notes": "", + "corrected_text": "configuration instructions", + "actualLine": 216, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "新流程说明:", + "line": 221, + "count": 3, + "notes": "", + "corrected_text": "New process description:", + "actualLine": 221, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "用户点击Discord登录", + "line": 224, + "count": 4, + "notes": "", + "corrected_text": "Users click on Discord to log in", + "actualLine": 224, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "跳转到Discord授权页面", + "line": 225, + "count": 4, + "notes": "", + "corrected_text": "Go to the Discord license page", + "actualLine": 225, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "回调路由获取code并重定向到 /login?discord_code=xxx", + "line": 227, + "count": 4, + "notes": "", + "corrected_text": "Callback route gets code and redirects to /login? discord_code = xxx", + "actualLine": 227, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "前端登录页面检测到code,调用后端API完成登录", + "line": 228, + "count": 4, + "notes": "", + "corrected_text": "The front-end login page detects the code, and calls the back-end API to complete the login.", + "actualLine": 228, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "环境变量配置:", + "line": 233, + "count": 3, + "notes": "", + "corrected_text": "Environment variables:", + "actualLine": 233, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "http://localhost:3000/api/auth/discord/callback", + "line": 244, + "count": 3, + "notes": "", + "corrected_text": "Http://localhost:3000/api/auth/discord/callback", + "actualLine": 244, + "replacementType": "jsx-text" + }, + { + "route": "test-discord", + "file": "src/app/test-discord/page.tsx", + "componentOrFn": "TestDiscordPage", + "kind": "text", + "keyOrLocator": "div", + "text": "API接口:", + "line": 248, + "count": 3, + "notes": "", + "corrected_text": "API interface:", + "actualLine": 248, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "图片裁剪组件测试", + "line": 61, + "count": 3, + "notes": "", + "corrected_text": "Image cropping test", + "actualLine": 61, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试各种图片裁剪功能和预设配置", + "line": 64, + "count": 3, + "notes": "", + "corrected_text": "Test various image cropping features and preset configurations", + "actualLine": 64, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "重置", + "line": 87, + "count": 3, + "notes": "", + "corrected_text": "reset", + "actualLine": 87, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "已选择:", + "line": 92, + "count": 3, + "notes": "", + "corrected_text": "Selected:", + "actualLine": 92, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "选择裁剪预设", + "line": 101, + "count": 2, + "notes": "", + "corrected_text": "Select crop preset", + "actualLine": 101, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "裁剪操作", + "line": 123, + "count": 2, + "notes": "", + "corrected_text": "cropping operation", + "actualLine": 123, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "打开高级裁剪弹窗", + "line": 129, + "count": 3, + "notes": "", + "corrected_text": "Open the advanced cropping pop-up", + "actualLine": 129, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "打开简单裁剪弹窗", + "line": 135, + "count": 3, + "notes": "", + "corrected_text": "Open the simple crop pop-up window", + "actualLine": 135, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "内联裁剪器", + "line": 144, + "count": 2, + "notes": "", + "corrected_text": "Internal connection clipper", + "actualLine": 144, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "裁剪结果", + "line": 157, + "count": 2, + "notes": "", + "corrected_text": "crop result", + "actualLine": 157, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "下载", + "line": 175, + "count": 4, + "notes": "", + "corrected_text": "download", + "actualLine": 175, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "text", + "keyOrLocator": "div", + "text": "原图预览", + "line": 186, + "count": 2, + "notes": "", + "corrected_text": "Original image preview", + "actualLine": 186, + "replacementType": "jsx-text" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "原图", + "line": 190, + "count": 1, + "notes": "", + "corrected_text": "original image", + "actualLine": 190, + "replacementType": "jsx-attribute" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "title", + "keyOrLocator": "ImageCropModal.title", + "text": "高级图片裁剪", + "line": 206, + "count": 1, + "notes": "", + "corrected_text": "Advanced image crop", + "actualLine": 206, + "replacementType": "jsx-attribute" + }, + { + "route": "test-image-crop", + "file": "src/app/test-image-crop/page.tsx", + "componentOrFn": "TestImageCropPage", + "kind": "title", + "keyOrLocator": "SimpleImageCropModal.title", + "text": "简单图片裁剪", + "line": 222, + "count": 1, + "notes": "", + "corrected_text": "Simple image cropping", + "actualLine": 222, + "replacementType": "jsx-attribute" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "MSW 状态", + "line": 55, + "count": 2, + "notes": "", + "corrected_text": "MSW status", + "actualLine": 55, + "replacementType": "jsx-text" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "页面信息", + "line": 60, + "count": 2, + "notes": "", + "corrected_text": "page information", + "actualLine": 60, + "replacementType": "jsx-text" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "导航测试", + "line": 65, + "count": 2, + "notes": "", + "corrected_text": "Navigation test", + "actualLine": 65, + "replacementType": "jsx-text" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "直接导航到 /profile", + "line": 71, + "count": 3, + "notes": "", + "corrected_text": "Navigate directly to /profile", + "actualLine": 71, + "replacementType": "jsx-text" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "编程式导航到 /profile", + "line": 77, + "count": 3, + "notes": "", + "corrected_text": "Programmatically navigate to /profile", + "actualLine": 77, + "replacementType": "jsx-text" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "说明", + "line": 83, + "count": 2, + "notes": "", + "corrected_text": "explain", + "actualLine": 83, + "replacementType": "jsx-text" + }, + { + "route": "test-middleware", + "file": "src/app/test-middleware/page.tsx", + "componentOrFn": "TestMiddlewarePage", + "kind": "text", + "keyOrLocator": "div", + "text": "如果点击上述按钮导航到 /profile 时没有在控制台看到 middleware 日志, 说明 MSW 或其他因素阻止了 middleware 的执行。", + "line": 85, + "count": 2, + "notes": "", + "corrected_text": "If you click the above button to navigate to /profile and do not see the middleware log in the console, MSW or something else is preventing middleware from executing.", + "actualLine": 85, + "replacementType": "jsx-text" + }, + { + "route": "test-s3-upload", + "file": "src/app/test-s3-upload/page.tsx", + "componentOrFn": "TestS3UploadPage", + "kind": "text", + "keyOrLocator": "div", + "text": "AWS S3 上传测试", + "line": 9, + "count": 3, + "notes": "", + "corrected_text": "AWS S3 Upload Test", + "actualLine": 9, + "replacementType": "jsx-text" + }, + { + "route": "test-s3-upload", + "file": "src/app/test-s3-upload/page.tsx", + "componentOrFn": "TestS3UploadPage", + "kind": "text", + "keyOrLocator": "div", + "text": "测试使用 AWS S3 SDK 的文件上传功能", + "line": 12, + "count": 3, + "notes": "", + "corrected_text": "Testing the file upload feature using the AWS S3 SDK", + "actualLine": 12, + "replacementType": "jsx-text" + }, + { + "route": "test-s3-upload", + "file": "src/app/test-s3-upload/page.tsx", + "componentOrFn": "TestS3UploadPage", + "kind": "text", + "keyOrLocator": "div", + "text": "Hook 使用方法", + "line": 20, + "count": 3, + "notes": "", + "corrected_text": "How to use Hook", + "actualLine": 20, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/layout/Sidebar.tsx", + "componentOrFn": "Sidebar", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "fold", + "line": 95, + "count": 1, + "notes": "", + "corrected_text": "Fold", + "actualLine": 98, + "replacementType": "jsx-attribute" + }, + { + "route": "shared", + "file": "src/components/layout/Sidebar.tsx", + "componentOrFn": "Sidebar", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "expand", + "line": 97, + "count": 1, + "notes": "", + "corrected_text": "Expand", + "actualLine": 100, + "replacementType": "jsx-attribute" + }, + { + "route": "shared", + "file": "src/components/layout/TopBarWithoutLogin.tsx", + "componentOrFn": "TopbarWithoutLogin", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "logo", + "line": 34, + "count": 2, + "notes": "", + "corrected_text": "Logo", + "actualLine": 34, + "replacementType": "jsx-attribute" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "S3 文件上传演示", + "line": 65, + "count": 1, + "notes": "", + "corrected_text": "S3 file upload demo", + "actualLine": 65, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "已选择文件:", + "line": 84, + "count": 2, + "notes": "", + "corrected_text": "Selected file:", + "actualLine": 84, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "取消上传", + "line": 106, + "count": 2, + "notes": "", + "corrected_text": "Cancel upload", + "actualLine": 106, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "重置", + "line": 116, + "count": 2, + "notes": "", + "corrected_text": "reset", + "actualLine": 116, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "上传进度:", + "line": 130, + "count": 3, + "notes": "", + "corrected_text": "Upload progress:", + "actualLine": 130, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "重试次数:", + "line": 137, + "count": 3, + "notes": "", + "corrected_text": "Number of retries:", + "actualLine": 137, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "上传错误", + "line": 146, + "count": 3, + "notes": "", + "corrected_text": "Upload error", + "actualLine": 146, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "功能特性:", + "line": 153, + "count": 2, + "notes": "", + "corrected_text": "Functional features:", + "actualLine": 153, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 真实的 AWS S3 上传(使用 STS token)", + "line": 155, + "count": 2, + "notes": "", + "corrected_text": "✅ Real AWS S3 upload (using STS token)", + "actualLine": 155, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 实时上传进度追踪", + "line": 156, + "count": 2, + "notes": "", + "corrected_text": "✅ real-time upload progress tracking", + "actualLine": 156, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 自动重试机制(最多3次,间隔2秒)", + "line": 157, + "count": 2, + "notes": "", + "corrected_text": "✅ Automatic retry mechanism (up to 3 times at 2-second intervals)", + "actualLine": 157, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 支持取消上传", + "line": 158, + "count": 2, + "notes": "", + "corrected_text": "✅ Support cancel upload", + "actualLine": 158, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 完整的错误处理", + "line": 159, + "count": 2, + "notes": "", + "corrected_text": "✅ Complete error handling", + "actualLine": 159, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ TypeScript 类型支持", + "line": 160, + "count": 2, + "notes": "", + "corrected_text": "✅ TypeScript type support", + "actualLine": 160, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 唯一文件名生成", + "line": 161, + "count": 2, + "notes": "", + "corrected_text": "✅ unique filename generation", + "actualLine": 161, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/S3UploadDemo.tsx", + "componentOrFn": "S3UploadDemo", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 预签名 URL 安全上传", + "line": 162, + "count": 2, + "notes": "", + "corrected_text": "✅ Presigned URL secure upload", + "actualLine": 162, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/abandon-creation-dialog.tsx", + "componentOrFn": "AbandonCreationDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "放弃创作", + "line": 40, + "count": 1, + "notes": "", + "corrected_text": "Give up creation", + "actualLine": 40, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/abandon-creation-dialog.tsx", + "componentOrFn": "AbandonCreationDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "选择退出或重新生图片,已经创作的图片将消失,同时消耗1次创作次数。", + "line": 43, + "count": 1, + "notes": "", + "corrected_text": "If you choose to exit or regenerate the image, the already created image will disappear, and the number of creations will be consumed once.", + "actualLine": 43, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/abandon-creation-dialog.tsx", + "componentOrFn": "AbandonCreationDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "取消", + "line": 46, + "count": 1, + "notes": "", + "corrected_text": "cancel", + "actualLine": 46, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/album-price-setting.tsx", + "componentOrFn": "AlbumPriceSetting", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "对话者通过付费方式解锁角色的图片,可以增加创作者的收入分成", + "line": 115, + "count": 1, + "notes": "", + "corrected_text": "The interlocutor pays to unlock the character's image, which can increase the creator's revenue share", + "actualLine": 151, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/album-price-setting.tsx", + "componentOrFn": "AlbumPriceSetting", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "Crushlevel平台会从每张图片的销售收入中分成20%作为平台服务费", + "line": 116, + "count": 1, + "notes": "", + "corrected_text": "The Crushlevel platform will take 20% of the sales revenue of each image as a platform service fee.", + "actualLine": 152, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/album-price-setting.tsx", + "componentOrFn": "AlbumPriceSetting", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "设置若干免费图片,可以吸引对话者与你的虚拟角色互动", + "line": 117, + "count": 1, + "notes": "", + "corrected_text": "Set up several free images to attract interlocutors to interact with your avatar", + "actualLine": 153, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/album-price-setting.tsx", + "componentOrFn": "AlbumPriceSetting", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "diamond", + "line": 180, + "count": 3, + "notes": "", + "corrected_text": "Diamond", + "actualLine": 216, + "replacementType": "jsx-attribute" + }, + { + "route": "shared", + "file": "src/components/features/charge-drawer.tsx", + "componentOrFn": "handlePayment", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "支付失败,请重试", + "line": 97, + "count": 1, + "notes": "", + "corrected_text": "Payment failure, please try again", + "actualLine": 97, + "replacementType": "function-arg" + }, + { + "route": "shared", + "file": "src/components/features/coin-insufficient-dialog.tsx", + "componentOrFn": "QuestionIcon", + "kind": "text", + "keyOrLocator": "Tooltip", + "text": "文本消息价格是指与角色进行文本消息对话的价格,含发送语音,含发送图片,发送礼物。", + "line": 36, + "count": 1, + "notes": "", + "corrected_text": "The text message price refers to the price of a text message conversation with a character, including sending voice, sending pictures, and sending gifts.", + "actualLine": 36, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/coin-insufficient-dialog.tsx", + "componentOrFn": "QuestionIcon", + "kind": "text", + "keyOrLocator": "Tooltip", + "text": "语音通话消息价格是指与角色进行语音电话对话的价格,按条计算", + "line": 37, + "count": 1, + "notes": "", + "corrected_text": "Voice call message price refers to the price of a voice call conversation with the character, calculated by item", + "actualLine": 37, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "设备ID (sd)", + "line": 45, + "count": 1, + "notes": "", + "corrected_text": "Device ID (sd)", + "actualLine": 45, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "存储在cookie中,字段名为'sd',在请求头中作为'AUTH_DID'发送", + "line": 50, + "count": 1, + "notes": "", + "corrected_text": "Stored in a cookie, field named'sd ', sent as AUTH_DID' in the request header", + "actualLine": 50, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "认证令牌 (st)", + "line": 55, + "count": 1, + "notes": "", + "corrected_text": "Authentication token (st)", + "actualLine": 55, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "存储在cookie中,字段名为'st',在请求头中作为'AUTH_TK'发送", + "line": 60, + "count": 1, + "notes": "", + "corrected_text": "Stored in a cookie, field named'st ', sent as AUTH_TK in the request header", + "actualLine": 60, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "刷新设备ID", + "line": 70, + "count": 1, + "notes": "", + "corrected_text": "Refresh device ID", + "actualLine": 70, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "清除所有数据", + "line": 77, + "count": 1, + "notes": "", + "corrected_text": "Clear all data", + "actualLine": 77, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "• 用户第一次访问时自动生成", + "line": 84, + "count": 1, + "notes": "", + "corrected_text": "Automatically generated when a user visits for the first time", + "actualLine": 84, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "• 包含时间戳、随机字符串和浏览器信息", + "line": 85, + "count": 1, + "notes": "", + "corrected_text": "• Contains timestamp, random string and browser information", + "actualLine": 85, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "• 存储在cookie中,有效期365天", + "line": 86, + "count": 1, + "notes": "", + "corrected_text": "Store in a cookie with a valid period of 365 days", + "actualLine": 86, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "• 用于设备识别和安全验证", + "line": 87, + "count": 1, + "notes": "", + "corrected_text": "• For device identification and security verification", + "actualLine": 87, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/features/device-info.tsx", + "componentOrFn": "DeviceInfo", + "kind": "text", + "keyOrLocator": "Card", + "text": "• 退出登录时不会被清除(只有clearAll才会清除)", + "line": 88, + "count": 1, + "notes": "", + "corrected_text": "• Will not be cleared when logging out (only clearAll will be cleared)", + "actualLine": 88, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/ui/empty.tsx", + "componentOrFn": "Empty", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "empty", + "line": 10, + "count": 1, + "notes": "", + "corrected_text": "Empty", + "actualLine": 10, + "replacementType": "jsx-attribute" + }, + { + "route": "shared", + "file": "src/components/ui/image-crop.tsx", + "componentOrFn": "ImageCrop", + "kind": "text", + "keyOrLocator": "div", + "text": "正在加载图片...", + "line": 206, + "count": 2, + "notes": "", + "corrected_text": "Loading pictures...", + "actualLine": 206, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/ui/image-crop.tsx", + "componentOrFn": "ImageCrop", + "kind": "text", + "keyOrLocator": "div", + "text": "缩放:", + "line": 256, + "count": 3, + "notes": "", + "corrected_text": "Zoom:", + "actualLine": 256, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/ui/image-crop.tsx", + "componentOrFn": "ImageCrop", + "kind": "text", + "keyOrLocator": "div", + "text": "旋转:", + "line": 288, + "count": 3, + "notes": "", + "corrected_text": "Rotate:", + "actualLine": 288, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/ui/select.tsx", + "componentOrFn": "SelectItem", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "check", + "line": 157, + "count": 1, + "notes": "", + "corrected_text": "Check", + "actualLine": 157, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/contact", + "file": "src/app/(main)/contact/contact-page.tsx", + "componentOrFn": "ContactCard", + "kind": "alt", + "keyOrLocator": "img.alt", + "text": "heart", + "line": 80, + "count": 1, + "notes": "", + "corrected_text": "Heart", + "actualLine": 80, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/contact", + "file": "src/app/(main)/contact/contact-page.tsx", + "componentOrFn": "EmptyState", + "kind": "alt", + "keyOrLocator": "img.alt", + "text": "empty", + "line": 149, + "count": 1, + "notes": "", + "corrected_text": "Empty", + "actualLine": 149, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/crushcoin", + "file": "src/app/(main)/crushcoin/crushcoin-page.tsx", + "componentOrFn": "CrushCoinPage", + "kind": "text", + "keyOrLocator": "div", + "text": "consecutive days", + "line": 20, + "count": 3, + "notes": "", + "corrected_text": "Consecutive days", + "actualLine": 20, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/about", + "file": "src/app/(auth)/about/page.tsx", + "componentOrFn": "AboutPage", + "kind": "alt", + "keyOrLocator": "img.alt", + "text": "banner", + "line": 12, + "count": 1, + "notes": "", + "corrected_text": "Banner", + "actualLine": 12, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/explore", + "file": "src/app/(main)/explore/page.tsx", + "componentOrFn": "ExplorePage", + "kind": "text", + "keyOrLocator": "div", + "text": "第三期功能正在打磨中, *&*……*……%……%……&%&……%&%……&%&……。", + "line": 9, + "count": 2, + "notes": "", + "corrected_text": "The third phase function is being polished, * & *... *...%...%... &% &...% &%... &% &...", + "actualLine": 9, + "replacementType": "jsx-text" + }, + { + "route": "(main)/explore", + "file": "src/app/(main)/explore/page.tsx", + "componentOrFn": "ExplorePage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "logo", + "line": 7, + "count": 1, + "notes": "", + "corrected_text": "Logo", + "actualLine": 7, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/leaderboard", + "file": "src/app/(main)/leaderboard/leaderboard-page.tsx", + "componentOrFn": "LeaderboardPage", + "kind": "text", + "keyOrLocator": "div", + "text": "热聊榜以AI聊天会话数高低排名。", + "line": 76, + "count": 2, + "notes": "", + "corrected_text": "The hot chat list is ranked by the number of AI chat sessions.", + "actualLine": 76, + "replacementType": "jsx-text" + }, + { + "route": "(main)/leaderboard", + "file": "src/app/(main)/leaderboard/leaderboard-page.tsx", + "componentOrFn": "LeaderboardPage", + "kind": "text", + "keyOrLocator": "div", + "text": "心动榜以AI角色所有对话者产生的心动值之和的高低排名。", + "line": 77, + "count": 2, + "notes": "", + "corrected_text": "The heart list is ranked by the sum of the heart values generated by all the interlocutors of the AI character.", + "actualLine": 77, + "replacementType": "jsx-text" + }, + { + "route": "(main)/leaderboard", + "file": "src/app/(main)/leaderboard/leaderboard-page.tsx", + "componentOrFn": "LeaderboardPage", + "kind": "text", + "keyOrLocator": "div", + "text": "礼物榜以AI角色所收到礼物打赏价值之和排名。", + "line": 78, + "count": 2, + "notes": "", + "corrected_text": "The gift list is ranked by the sum of the gift value received by the AI character.", + "actualLine": 78, + "replacementType": "jsx-text" + }, + { + "route": "(main)/leaderboard", + "file": "src/app/(main)/leaderboard/leaderboard-page.tsx", + "componentOrFn": "LeaderboardPage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "bg", + "line": 60, + "count": 1, + "notes": "", + "corrected_text": "Bg", + "actualLine": 60, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/profile", + "file": "src/app/(main)/profile/profile-page.tsx", + "componentOrFn": "ProfilePage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "gender", + "line": 59, + "count": 1, + "notes": "", + "corrected_text": "Gender", + "actualLine": 59, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "语音波纹动画效果展示", + "line": 31, + "count": 2, + "notes": "", + "corrected_text": "Voice ripple animation effect display", + "actualLine": 31, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "技术实现说明", + "line": 35, + "count": 3, + "notes": "", + "corrected_text": "Technology implementation note", + "actualLine": 35, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "✅ 使用纯CSS实现:", + "line": 37, + "count": 4, + "notes": "", + "corrected_text": "✅ Implemented using pure CSS:", + "actualLine": 37, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "性能优秀,GPU加速,文件体积小", + "line": 37, + "count": 4, + "notes": "", + "corrected_text": "Excellent performance, GPU acceleration, small file size", + "actualLine": 37, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "从左到右整体渐变(灰色→白色→灰色)", + "line": 38, + "count": 4, + "notes": "", + "corrected_text": "Overall gradual change from left to right (gray → white → gray)", + "actualLine": 38, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "声波从中心向两边扩散,模拟真实音频传播", + "line": 39, + "count": 4, + "notes": "", + "corrected_text": "Sound waves spread from the center to both sides, simulating real audio transmission", + "actualLine": 39, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "⏸️ 停止状态:", + "line": 40, + "count": 4, + "notes": "", + "corrected_text": "⏸️ Stop state:", + "actualLine": 40, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "动画开始/停止时使用CSS transition平滑过渡", + "line": 41, + "count": 4, + "notes": "", + "corrected_text": "Use CSS transitions to smooth transitions when animation starts/stops", + "actualLine": 41, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "语音波纹效果(灰色到白色渐变)", + "line": 61, + "count": 3, + "notes": "", + "corrected_text": "Voice ripple effect (gradual change from gray to white)", + "actualLine": 61, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "从左到右:灰色 → 白色 → 灰色的整体渐变效果", + "line": 74, + "count": 5, + "notes": "", + "corrected_text": "From left to right: gray → white → gray overall gradient effect", + "actualLine": 74, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "涟漪效果:中心音频更强,向两边逐渐减弱", + "line": 75, + "count": 5, + "notes": "", + "corrected_text": "Ripple effect: center audio is stronger, gradually fading to either side", + "actualLine": 75, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "不同声波条数量对比", + "line": 82, + "count": 3, + "notes": "", + "corrected_text": "Comparison of the number of different sonic strips", + "actualLine": 82, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "个条)", + "line": 86, + "count": 5, + "notes": "", + "corrected_text": "Article)", + "actualLine": 86, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "大尺寸展示效果", + "line": 104, + "count": 3, + "notes": "", + "corrected_text": "Large size display effect", + "actualLine": 104, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "大尺寸下的语音波纹效果,适合作为页面主要视觉元素", + "line": 114, + "count": 4, + "notes": "", + "corrected_text": "The large-scale voice ripple effect is suitable for use as the main visual element of the page", + "actualLine": 114, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "使用方法", + "line": 121, + "count": 3, + "notes": "", + "corrected_text": "How to use", + "actualLine": 121, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "特性:", + "line": 143, + "count": 4, + "notes": "", + "corrected_text": "Features:", + "actualLine": 143, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "整体从左到右渐变:灰色 → 白色 → 灰色", + "line": 145, + "count": 4, + "notes": "", + "corrected_text": "Overall gradual change from left to right: gray → white → gray", + "actualLine": 145, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "涟漪扩散:声波从中心向两边扩散,延迟递增", + "line": 146, + "count": 4, + "notes": "", + "corrected_text": "Ripple diffusion: Sound waves spread from the center to both sides, with increasing delay", + "actualLine": 146, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "中心增强:中心声波比两边高40%,模拟音源位置", + "line": 147, + "count": 4, + "notes": "", + "corrected_text": "Center enhancement: The center sound wave is 40% higher than the two sides, simulating the sound source position", + "actualLine": 147, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "使用CSS transition实现平滑过渡", + "line": 149, + "count": 4, + "notes": "", + "corrected_text": "Use CSS transitions for smooth transitions", + "actualLine": 149, + "replacementType": "jsx-text" + }, + { + "route": "(main)/test-voice-wave", + "file": "src/app/(main)/test-voice-wave/page.tsx", + "componentOrFn": "TestVoiceWavePage", + "kind": "text", + "keyOrLocator": "div", + "text": "纯CSS实现,性能优秀", + "line": 150, + "count": 4, + "notes": "", + "corrected_text": "Pure CSS implementation, excellent performance", + "actualLine": 150, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/login", + "file": "src/app/(auth)/login/components/DiscordButton.tsx", + "componentOrFn": "DiscordButton", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "Discord登录成功!", + "line": 63, + "count": 1, + "notes": "", + "corrected_text": "Login successful", + "actualLine": 63, + "replacementType": "function-arg" + }, + { + "route": "(auth)/login", + "file": "src/app/(auth)/login/components/ImageCarousel.tsx", + "componentOrFn": "ImageCarousel", + "kind": "text", + "keyOrLocator": "div", + "text": "没有图片", + "line": 39, + "count": 1, + "notes": "", + "corrected_text": "No picture.", + "actualLine": 39, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/login", + "file": "src/app/(auth)/login/components/login-form.tsx", + "componentOrFn": "LoginForm", + "kind": "toast", + "keyOrLocator": "toast.info", + "text": "Google登录", + "line": 10, + "count": 1, + "notes": "", + "corrected_text": "Google Sign In", + "actualLine": 10, + "replacementType": "function-arg" + }, + { + "route": "(auth)/login", + "file": "src/app/(auth)/login/components/login-form.tsx", + "componentOrFn": "LoginForm", + "kind": "toast", + "keyOrLocator": "toast.info", + "text": "Apple登录", + "line": 16, + "count": 1, + "notes": "", + "corrected_text": "Apple Sign In", + "actualLine": 16, + "replacementType": "function-arg" + }, + { + "route": "shared", + "file": "src/components/layout/components/ChatConversationsDeleteDialog.tsx", + "componentOrFn": "ChatConversationsDeleteDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "删除消息", + "line": 31, + "count": 1, + "notes": "", + "corrected_text": "Delete message", + "actualLine": 31, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/layout/components/ChatConversationsDeleteDialog.tsx", + "componentOrFn": "ChatConversationsDeleteDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "删除全部消息后,将清空所有的消息记录", + "line": 33, + "count": 1, + "notes": "", + "corrected_text": "After deleting all messages, all message records will be cleared", + "actualLine": 33, + "replacementType": "jsx-text" + }, + { + "route": "shared", + "file": "src/components/layout/components/ChatSearchResults.tsx", + "componentOrFn": "PersonItem", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "heart", + "line": 99, + "count": 2, + "notes": "", + "corrected_text": "Heart", + "actualLine": 99, + "replacementType": "jsx-attribute" + }, + { + "route": "shared", + "file": "src/components/layout/components/ChatSidebarAction.tsx", + "componentOrFn": "ChatSidebarAction", + "kind": "text", + "keyOrLocator": "DropdownMenu", + "text": "iconfont icon-Search", + "line": 49, + "count": 1, + "notes": "", + "corrected_text": "Iconfont icon-Search", + "actualLine": 49, + "replacementType": "jsx-expression" + }, + { + "route": "shared", + "file": "src/components/layout/components/Notice.tsx", + "componentOrFn": "Notice", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "notice", + "line": 57, + "count": 1, + "notes": "", + "corrected_text": "Notice", + "actualLine": 57, + "replacementType": "jsx-attribute" + }, + { + "route": "(auth)/login/fields", + "file": "src/app/(auth)/login/fields/fields-page.tsx", + "componentOrFn": "FieldsPage", + "kind": "text", + "keyOrLocator": "div", + "text": "submit", + "line": 237, + "count": 4, + "notes": "", + "corrected_text": "Submit", + "actualLine": 237, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/policy/tos", + "file": "src/app/(auth)/policy/tos/page.tsx", + "componentOrFn": "TermsOfServicePage", + "kind": "text", + "keyOrLocator": "div", + "text": "Welcome to the Crushlevel application (hereinafter referred to as \"this App\") and related website (Crushlevel.ai, hereinafter referred to as \"this Website\"). This User Agreement (hereinafter referred to as \"this Agreement\") is a legally binding agreement between you (hereinafter referred to as the \"User\") and the operator of Crushlevel (hereinafter referred to as \"We,\" \"Us,\" or \"Our\") regarding your use of this App and this Website. Before registering for or using this App and this Website, please read this Agreement carefully and understand its contents in full. If you have any questions regarding this Agreement, you should consult Us. If you do not agree to any part of this Agreement, you should immediately cease registration or use of this App and this Website. Once you register for or use this App and this Website, it means that you have fully understood and agreed to all the terms of this Agreement.", + "line": 16, + "count": 4, + "notes": "", + "corrected_text": "Welcome to the Crushlevel application (hereinafter referred to as \"this App\") and related website (Crushlevel.ai, hereinafter referred to as \"this Website\"). This User Agreement (hereinafter referred to as \"this Agreement\") is a legally binding agreement between you (hereinafter referred to as the \"User\") and the operator of Crushlevel (hereinafter referred to as \"We,\" \"Us,\" or \"Our\") regarding your use of this App and this Website. Before registering for or using this App and this Website, please read this Agreement carefully and understand it's contents in full. If you have any questions regarding this Agreement, you should consult Us. If you do not agree to any part of this Agreement, you should immediately cease registration or use of this App and this Website. Once you register for or use this App and this Website, it means that you have fully understood and agreed to all the terms of this Agreement.", + "actualLine": 16, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/policy/tos", + "file": "src/app/(auth)/policy/tos/page.tsx", + "componentOrFn": "TermsOfServicePage", + "kind": "text", + "keyOrLocator": "div", + "text": "You shall ensure that the registration information provided is true, accurate, and complete, and promptly update your registration information to ensure its validity. If, due to registration information provided by you being untrue, inaccurate, incomplete, or not updated in a timely manner, We are unable to provide you with corresponding services or any other losses arise, you shall bear full responsibility.", + "line": 33, + "count": 6, + "notes": "", + "corrected_text": "You shall ensure that the registration information provided is true, accurate, and complete, and promptly update your registration information to ensure it's validity. If, due to registration information provided by you being untrue, inaccurate, incomplete, or not updated in a timely manner, We are unable to provide you with corresponding services or any other losses arise, you shall bear full responsibility.", + "actualLine": 33, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/share/[userId]", + "file": "src/app/(auth)/share/[userId]/share-page.tsx", + "componentOrFn": "SharePage", + "kind": "text", + "keyOrLocator": "div", + "text": "likes", + "line": 73, + "count": 6, + "notes": "", + "corrected_text": "Likes", + "actualLine": 73, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/policy/recharge", + "file": "src/app/(auth)/policy/recharge/page.tsx", + "componentOrFn": "RechargeAgreementPage", + "kind": "text", + "keyOrLocator": "div", + "text": "This Recharge Service Agreement (hereinafter referred to as \"this Agreement\") is entered into between you and the operator of Crushlevel (hereinafter referred to as the \"Platform\") and/or its affiliates (hereinafter referred to as the \"Company\"). The Platform shall provide services to you in accordance with the provisions of this Agreement and the operating rules issued from time to time (hereinafter referred to as the \"Services\"). For the purpose of providing better services to users, you, as the service user (i.e., the account user who places an order to purchase the Platform's virtual currency, hereinafter referred to as \"you\"), shall carefully read and fully understand this Agreement before starting to use the Services. Among them, clauses that exempt or limit the Platform's liability, dispute resolution methods, jurisdiction and other important contents will be highlighted in", + "line": 24, + "count": 4, + "notes": "", + "corrected_text": "This Recharge Service Agreement (hereinafter referred to as \"this Agreement\") is entered into between you and the operator of Crushlevel (hereinafter referred to as the \"Platform\") and/or it's affiliates (hereinafter referred to as the \"Company\"). The Platform shall provide services to you in accordance with the provisions of this Agreement and the operating rules issued from time to time (hereinafter referred to as the \"Services\"). For the purpose of providing better services to users, you, as the service user (i.e., the account user who places an order to purchase the Platform's virtual currency, hereinafter referred to as \"you\"), shall carefully read and fully understand this Agreement before starting to use the Services. Among them, clauses that exempt or limit the Platform's liability, dispute resolution methods, jurisdiction and other important contents will be highlighted in", + "actualLine": 24, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/policy/recharge", + "file": "src/app/(auth)/policy/recharge/page.tsx", + "componentOrFn": "RechargeAgreementPage", + "kind": "text", + "keyOrLocator": "div", + "text": "You understand and agree that the Services are provided in accordance with the current state achievable under existing technologies and conditions. The Platform will make its best efforts to provide the Services to you and ensure the security and stability of the Services. However, you also know and acknowledge that the Platform cannot foresee and prevent technical and other risks at all times or at all times, including but not limited to service interruptions, delays, errors or data loss caused by force majeure (such as natural disasters, wars, public health emergencies, etc.), network reasons (such as network congestion, hacker attacks, server failures, etc.), third-party service defects (such as failures of third-party payment institutions, changes in app store policies, etc.), revisions to laws and regulations or adjustments to regulatory policies, etc. In the event of such circumstances, the Platform will make its best commercial efforts to improve the situation, but shall not be obligated to bear any legal liability to you or other third parties, unless such losses are caused by the intentional acts or gross negligence of the Platform.", + "line": 324, + "count": 7, + "notes": "", + "corrected_text": "You understand and agree that the Services are provided in accordance with the current state achievable under existing technologies and conditions. The Platform will make it's best efforts to provide the Services to you and ensure the security and stability of the Services. However, you also know and acknowledge that the Platform cannot foresee and prevent technical and other risks at all times or at all times, including but not limited to service interruptions, delays, errors or data loss caused by force majeure (such as natural disasters, wars, public health emergencies, etc.), network reasons (such as network congestion, hacker attacks, server failures, etc.), third-party service defects (such as failures of third-party payment institutions, changes in app store policies, etc.), revisions to laws and regulations or adjustments to regulatory policies, etc. In the event of such circumstances, the Platform will makeit'ss best commercial efforts to improve the situation, but shall not be obligated to bear any legal liability to you or other third parties, unless such losses are caused by the intentional acts or gross negligence of the Platform.", + "actualLine": 324, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/policy/recharge", + "file": "src/app/(auth)/policy/recharge/page.tsx", + "componentOrFn": "RechargeAgreementPage", + "kind": "text", + "keyOrLocator": "div", + "text": "The Platform may conduct downtime maintenance, system upgrades and function adjustments on its own. If you are unable to use the Services normally due to this, the Platform will notify you of the maintenance/upgrade time and the scope of impact in advance through reasonable methods (except for emergency maintenance), and you agree that the Platform shall not bear legal liability for this. Any losses caused by your attempt to use the Services during the maintenance/upgrade period shall be borne by yourself.", + "line": 336, + "count": 7, + "notes": "", + "corrected_text": "The Platform may conduct downtime maintenance, system upgrades and function adjustments on it's own. If you are unable to use the Services normally due to this, the Platform will notify you of the maintenance/upgrade time and the scope of impact in advance through reasonable methods (except for emergency maintenance), and you agree that the Platform shall not bear legal liability for this. Any losses caused by your attempt to use the Services during the maintenance/upgrade period shall be borne by yourself.", + "actualLine": 336, + "replacementType": "jsx-text" + }, + { + "route": "(auth)/policy/recharge", + "file": "src/app/(auth)/policy/recharge/page.tsx", + "componentOrFn": "RechargeAgreementPage", + "kind": "text", + "keyOrLocator": "strong", + "text": "the Platform does not provide refund services for this part of the Virtual Currency", + "line": 305, + "count": 1, + "notes": "", + "corrected_text": "The Platform does not provide refund services for this part of the Virtual Currency", + "actualLine": 305, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "内容未保存", + "line": 80, + "count": 1, + "notes": "", + "corrected_text": "Unsaved changes", + "actualLine": 80, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "内容未保存,是否继续退出?", + "line": 81, + "count": 1, + "notes": "", + "corrected_text": "The content has not been saved, Continue to quit?", + "actualLine": 81, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "取消", + "line": 84, + "count": 1, + "notes": "", + "corrected_text": "Cancel", + "actualLine": 84, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "退出", + "line": 85, + "count": 1, + "notes": "", + "corrected_text": "Quit", + "actualLine": 85, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/CopyrightRuleModal.tsx", + "componentOrFn": "CopyrightRuleModal", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "请确认该虚拟角色是您的原创或同人创作,不侵犯他人的图像,IP或其他权利。", + "line": 26, + "count": 1, + "notes": "", + "corrected_text": "By clicking \"Confirm\", you represent and warrant that you are the original creator of this virtual character and that it does not infringe upon any third party's intellectual property rights or other legal rights.", + "actualLine": 26, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageForm.tsx", + "componentOrFn": "ImageForm", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "Create character successfully", + "line": 201, + "count": 1, + "notes": "", + "corrected_text": "Character created successfully!", + "actualLine": 214, + "replacementType": "function-arg" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageForm.tsx", + "componentOrFn": "ImageForm", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "avatar", + "line": 294, + "count": 1, + "notes": "", + "corrected_text": "Avatar", + "actualLine": 307, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageForm.tsx", + "componentOrFn": "ImageForm", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "为用户介绍该虚拟角色", + "line": 337, + "count": 1, + "notes": "", + "corrected_text": "Introduce the virtual character to the user", + "actualLine": 350, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageGeneration.tsx", + "componentOrFn": "ImageGeneration", + "kind": "text", + "keyOrLocator": "div", + "text": "风格选择", + "line": 83, + "count": 3, + "notes": "", + "corrected_text": "Style selection", + "actualLine": 83, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageGeneration.tsx", + "componentOrFn": "ImageGeneration", + "kind": "text", + "keyOrLocator": "div", + "text": "描述", + "line": 109, + "count": 4, + "notes": "", + "corrected_text": "Description", + "actualLine": 109, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageGeneration.tsx", + "componentOrFn": "ImageGeneration", + "kind": "text", + "keyOrLocator": "div", + "text": "使用角色信息", + "line": 116, + "count": 4, + "notes": "", + "corrected_text": "Using Character Information", + "actualLine": 116, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageGeneration.tsx", + "componentOrFn": "ImageGeneration", + "kind": "text", + "keyOrLocator": "div", + "text": "AI生成可能需要几分钟时间,请耐心等待", + "line": 133, + "count": 3, + "notes": "", + "corrected_text": "AI generation may take a few minutes, please be patient.", + "actualLine": 133, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageGeneration.tsx", + "componentOrFn": "ImageGeneration", + "kind": "text", + "keyOrLocator": "div", + "text": "正在生成图片...", + "line": 148, + "count": 4, + "notes": "", + "corrected_text": "Generating image...", + "actualLine": 148, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageGeneration.tsx", + "componentOrFn": "ImageGeneration", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "描述你想要生成的角色图片,例如:beautiful anime girl with long blue hair, wearing school uniform...", + "line": 123, + "count": 1, + "notes": "", + "corrected_text": "Describe the character image you want to generate, for example: beautiful anime girl with long blue hair, wearing school uniform...", + "actualLine": 123, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImagePreview.tsx", + "componentOrFn": "ImagePreview", + "kind": "text", + "keyOrLocator": "div", + "text": "共", + "line": 128, + "count": 2, + "notes": "", + "corrected_text": "total", + "actualLine": 128, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImagePreview.tsx", + "componentOrFn": "ImagePreview", + "kind": "text", + "keyOrLocator": "div", + "text": "张图片", + "line": 128, + "count": 2, + "notes": "", + "corrected_text": "picture", + "actualLine": 128, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImagePreview.tsx", + "componentOrFn": "ImagePreview", + "kind": "text", + "keyOrLocator": "div", + "text": "已选中主图片", + "line": 130, + "count": 2, + "notes": "", + "corrected_text": "Main image selected", + "actualLine": 130, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageUpload.tsx", + "componentOrFn": "ImageUpload", + "kind": "text", + "keyOrLocator": "div", + "text": "支持 JPG、PNG、GIF 格式,单文件不超过", + "line": 154, + "count": 4, + "notes": "", + "corrected_text": "Support JPG, PNG, GIF format, single file no more than", + "actualLine": 154, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageUpload.tsx", + "componentOrFn": "ImageUpload", + "kind": "text", + "keyOrLocator": "div", + "text": "最多可上传", + "line": 158, + "count": 4, + "notes": "", + "corrected_text": "Up to upload", + "actualLine": 158, + "replacementType": "jsx-text" + }, + { + "route": "(main)/create", + "file": "src/app/(main)/create/components/ImageUpload.tsx", + "componentOrFn": "ImageUpload", + "kind": "text", + "keyOrLocator": "div", + "text": "个文件", + "line": 158, + "count": 4, + "notes": "", + "corrected_text": "files", + "actualLine": 158, + "replacementType": "jsx-text" + }, + { + "route": "(main)/contact", + "file": "src/app/(main)/contact/components/RenderContactStatusText.tsx", + "componentOrFn": "RenderContactStatusText", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "heart-rate", + "line": 35, + "count": 1, + "notes": "", + "corrected_text": "Heart-rate", + "actualLine": 35, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/contact", + "file": "src/app/(main)/contact/components/RenderContactStatusText.tsx", + "componentOrFn": "RenderContactStatusText", + "kind": "text", + "keyOrLocator": "div", + "text": "与你的心动值达到15.0摄氏度以上的角色作为最为排名对象", + "line": 53, + "count": 2, + "notes": "", + "corrected_text": "The character with your heart value above 15.0 ℃ will be the most ranked object.", + "actualLine": 53, + "replacementType": "jsx-text" + }, + { + "route": "(main)/contact", + "file": "src/app/(main)/contact/components/RenderContactStatusText.tsx", + "componentOrFn": "RenderContactStatusText", + "kind": "text", + "keyOrLocator": "div", + "text": "按照这些角色心动值总和进行排名", + "line": 54, + "count": 2, + "notes": "", + "corrected_text": "Rank according to the total heart value of these characters", + "actualLine": 54, + "replacementType": "jsx-text" + }, + { + "route": "(main)/contact", + "file": "src/app/(main)/contact/components/RenderContactStatusText.tsx", + "componentOrFn": "RenderContactStatusText", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "question-border", + "line": 49, + "count": 1, + "notes": "", + "corrected_text": "Question-border", + "actualLine": 49, + "replacementType": "jsx-attribute" + }, + { + "route": "/", + "file": "src/app/(main)/generate/components/GeneralBuyTimesDialog.tsx", + "componentOrFn": "GeneralBuyTimesDialog", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "buy times", + "line": 88, + "count": 1, + "notes": "", + "corrected_text": "Buy times", + "actualLine": 107, + "replacementType": "jsx-attribute" + }, + { + "route": "/", + "file": "src/app/(main)/generate/components/GeneralImageWithCountButton.tsx", + "componentOrFn": "GeneralImageWithCountButton", + "kind": "text", + "keyOrLocator": "Button", + "text": "会员创作次数 +10/月", + "line": 125, + "count": 1, + "notes": "", + "corrected_text": "Member creation times + 10/month", + "actualLine": 125, + "replacementType": "jsx-text" + }, + { + "route": "/", + "file": "src/app/(main)/generate/components/ReferenceUpload.tsx", + "componentOrFn": "validateFile", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "请选择图片文件", + "line": 38, + "count": 1, + "notes": "", + "corrected_text": "Please select an image file", + "actualLine": 38, + "replacementType": "function-arg" + }, + { + "route": "/", + "file": "src/app/(main)/generate/components/ReferenceUpload.tsx", + "componentOrFn": "validateFile", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "图片文件不能超过10MB", + "line": 43, + "count": 1, + "notes": "", + "corrected_text": "Image files cannot exceed 10MB.", + "actualLine": 43, + "replacementType": "function-arg" + }, + { + "route": "(main)/generate/image-2-background", + "file": "src/app/(main)/generate/image-2-background/image-page.tsx", + "componentOrFn": "Image2BackgroundPage", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "请描述形象的肤色、服饰、发型、五官、动作、背景等", + "line": 212, + "count": 1, + "notes": "", + "corrected_text": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "actualLine": 228, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/generate/image-2-background", + "file": "src/app/(main)/generate/image-2-background/image-page.tsx", + "componentOrFn": "Image2BackgroundPage", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "diamond", + "line": 240, + "count": 1, + "notes": "", + "corrected_text": "Diamond", + "actualLine": 256, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/crushcoin", + "file": "src/app/(main)/crushcoin/components/CheckInCard.tsx", + "componentOrFn": "CheckInCard", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "diamond", + "line": 60, + "count": 1, + "notes": "", + "corrected_text": "Diamond", + "actualLine": 60, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/crushcoin", + "file": "src/app/(main)/crushcoin/components/CheckInCard.tsx", + "componentOrFn": "CheckInCard", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "star", + "line": 69, + "count": 1, + "notes": "", + "corrected_text": "Star", + "actualLine": 69, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/crushcoin", + "file": "src/app/(main)/crushcoin/components/CheckInGrid.tsx", + "componentOrFn": "CheckInGrid", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "今日已签到", + "line": 25, + "count": 1, + "notes": "", + "corrected_text": "Signed in today", + "actualLine": 25, + "replacementType": "function-arg" + }, + { + "route": "(main)/generate/image-2-image", + "file": "src/app/(main)/generate/image-2-image/image-page.tsx", + "componentOrFn": "ImagePage", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "请描述形象的肤色、服饰、发型、五官、动作、背景等", + "line": 230, + "count": 1, + "notes": "", + "corrected_text": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "actualLine": 230, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/generate/image", + "file": "src/app/(main)/generate/image/generate-image-page.tsx", + "componentOrFn": "GenerateImagePage", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "请描述形象的肤色、服饰、发型、五官、动作、背景等", + "line": 183, + "count": 1, + "notes": "", + "corrected_text": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "actualLine": 183, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/generate/image-edit", + "file": "src/app/(main)/generate/image-edit/image-edit-page.tsx", + "componentOrFn": "ImageEditPage", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "请描述形象的肤色、服饰、发型、五官、动作、背景等", + "line": 216, + "count": 1, + "notes": "", + "corrected_text": "Please describe the character's skin color, clothing, hairstyle, facial features, movements, background, etc.", + "actualLine": 216, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/leaderboard", + "file": "src/app/(main)/leaderboard/components/TopHeader.tsx", + "componentOrFn": "TopHeader", + "kind": "text", + "keyOrLocator": "div", + "text": "暂无排行榜数据", + "line": 35, + "count": 3, + "notes": "", + "corrected_text": "No leaderboard data yet", + "actualLine": 35, + "replacementType": "jsx-text" + }, + { + "route": "/", + "file": "src/app/(main)/home/components/HomeHeader.tsx", + "componentOrFn": "HomeHeader", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "banner", + "line": 13, + "count": 1, + "notes": "", + "corrected_text": "Banner", + "actualLine": 13, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/profile", + "file": "src/app/(main)/profile/components/AvatarSetting.tsx", + "componentOrFn": "handleFileUpload", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "请选择图片文件", + "line": 33, + "count": 1, + "notes": "", + "corrected_text": "Please select an image file", + "actualLine": 33, + "replacementType": "function-arg" + }, + { + "route": "(main)/profile", + "file": "src/app/(main)/profile/components/AvatarSetting.tsx", + "componentOrFn": "handleFileUpload", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "不支持 GIF 格式,请选择 JPG、JPEG 或 PNG 格式的图片", + "line": 39, + "count": 1, + "notes": "", + "corrected_text": "GIF format is not supported, please select JPG, JPEG or PNG format images", + "actualLine": 39, + "replacementType": "function-arg" + }, + { + "route": "(main)/profile", + "file": "src/app/(main)/profile/components/AvatarSetting.tsx", + "componentOrFn": "handleFileUpload", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "图片文件不能超过10MB", + "line": 45, + "count": 1, + "notes": "", + "corrected_text": "Image files cannot exceed 10MB.", + "actualLine": 45, + "replacementType": "function-arg" + }, + { + "route": "(main)/profile", + "file": "src/app/(main)/profile/components/AvatarSetting.tsx", + "componentOrFn": "AvatarSetting", + "kind": "text", + "keyOrLocator": "div", + "text": "头像必须是 JPG、JPEG 或 PNG 格式,文件大小不能超过 10MB", + "line": 129, + "count": 4, + "notes": "", + "corrected_text": "The avatar must be in JPG, JPEG, or PNG format, and the file size cannot exceed 10MB.", + "actualLine": 129, + "replacementType": "jsx-text" + }, + { + "route": "(main)/profile", + "file": "src/app/(main)/profile/components/CharacterCardAdd.tsx", + "componentOrFn": "CharacterCardAdd", + "kind": "text", + "keyOrLocator": "div", + "text": "Add More Character", + "line": 32, + "count": 6, + "notes": "", + "corrected_text": "Add More Characters", + "actualLine": 32, + "replacementType": "jsx-text" + }, + { + "route": "(main)/wallet", + "file": "src/app/(main)/wallet/components/IncomeList.tsx", + "componentOrFn": "IncomeItem", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "diamond", + "line": 138, + "count": 2, + "notes": "", + "corrected_text": "Diamond", + "actualLine": 138, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/wallet", + "file": "src/app/(main)/wallet/components/RechargeList.tsx", + "componentOrFn": "handlePayment", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "支付失败,请重试", + "line": 73, + "count": 1, + "notes": "", + "corrected_text": "Payment failure, please try again", + "actualLine": 73, + "replacementType": "function-arg" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatBackground.tsx", + "componentOrFn": "ChatBackground", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "background", + "line": 14, + "count": 1, + "notes": "", + "corrected_text": "Background", + "actualLine": 14, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/CrushLevelAction.tsx", + "componentOrFn": "CrushLevelAction", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "heart", + "line": 220, + "count": 2, + "notes": "", + "corrected_text": "Heart", + "actualLine": 220, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/edit/[aiId]/image", + "file": "src/app/(main)/edit/[aiId]/image/image-page.tsx", + "componentOrFn": "ImageForm", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "avatar", + "line": 253, + "count": 1, + "notes": "", + "corrected_text": "Avatar", + "actualLine": 268, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/edit/[aiId]/image", + "file": "src/app/(main)/edit/[aiId]/image/image-page.tsx", + "componentOrFn": "ImageForm", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "为用户介绍该虚拟角色", + "line": 296, + "count": 1, + "notes": "", + "corrected_text": "Introduce the virtual character to the user", + "actualLine": 311, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/edit/[aiId]", + "file": "src/app/(main)/edit/[aiId]/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "内容未保存", + "line": 89, + "count": 1, + "notes": "", + "corrected_text": "Unsaved changes", + "actualLine": 89, + "replacementType": "jsx-text" + }, + { + "route": "(main)/edit/[aiId]", + "file": "src/app/(main)/edit/[aiId]/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "内容未保存,是否继续退出?", + "line": 90, + "count": 1, + "notes": "", + "corrected_text": "The content has not been saved, Continue to quit?", + "actualLine": 90, + "replacementType": "jsx-text" + }, + { + "route": "(main)/edit/[aiId]", + "file": "src/app/(main)/edit/[aiId]/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "取消", + "line": 93, + "count": 1, + "notes": "", + "corrected_text": "Cancel", + "actualLine": 93, + "replacementType": "jsx-text" + }, + { + "route": "(main)/edit/[aiId]", + "file": "src/app/(main)/edit/[aiId]/components/CloseIconButton.tsx", + "componentOrFn": "CloseIconButton", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "退出", + "line": 94, + "count": 1, + "notes": "", + "corrected_text": "Quit", + "actualLine": 94, + "replacementType": "jsx-text" + }, + { + "route": "/", + "file": "src/app/(main)/home/components/Meet/MeetList.tsx", + "componentOrFn": "ErrorState", + "kind": "text", + "keyOrLocator": "div", + "text": "加载失败", + "line": 74, + "count": 1, + "notes": "", + "corrected_text": "Load failed", + "actualLine": 74, + "replacementType": "jsx-text" + }, + { + "route": "/", + "file": "src/app/(main)/home/components/Meet/MeetList.tsx", + "componentOrFn": "ErrorState", + "kind": "text", + "keyOrLocator": "div", + "text": "网络连接异常,请稍后重试", + "line": 75, + "count": 1, + "notes": "", + "corrected_text": "The network connection is abnormal. Please try again later", + "actualLine": 75, + "replacementType": "jsx-text" + }, + { + "route": "/", + "file": "src/app/(main)/home/components/Meet/MeetList.tsx", + "componentOrFn": "ErrorState", + "kind": "text", + "keyOrLocator": "div", + "text": "重新加载", + "line": 80, + "count": 1, + "notes": "", + "corrected_text": "Reload", + "actualLine": 80, + "replacementType": "jsx-text" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/AlbumImageViewerAction.tsx", + "componentOrFn": "renderDefaultAction", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "默认图片", + "line": 156, + "count": 1, + "notes": "", + "corrected_text": "Default image", + "actualLine": 156, + "replacementType": "jsx-text" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/AlbumImageViewerAction.tsx", + "componentOrFn": "renderDefaultAction", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "设置为默认图片后,图片的解锁方式只能为“免费”", + "line": 159, + "count": 1, + "notes": "", + "corrected_text": "After setting as the default picture, the unlock method of the picture can only be \"free\".", + "actualLine": 159, + "replacementType": "jsx-text" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/AlbumImageViewerAction.tsx", + "componentOrFn": "renderDefaultAction", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "取消", + "line": 162, + "count": 1, + "notes": "", + "corrected_text": "Cancel", + "actualLine": 162, + "replacementType": "jsx-text" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/AlbumImageViewerAction.tsx", + "componentOrFn": "renderDefaultAction", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "确定", + "line": 168, + "count": 1, + "notes": "", + "corrected_text": "Confirm", + "actualLine": 168, + "replacementType": "jsx-text" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/AlbumList.tsx", + "componentOrFn": "handleUnlock", + "kind": "toast", + "keyOrLocator": "toast.success", + "text": "解锁成功!", + "line": 69, + "count": 1, + "notes": "", + "corrected_text": "Unlocked successfully!", + "actualLine": 69, + "replacementType": "function-arg" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/GiftGrid.tsx", + "componentOrFn": "GiftGrid", + "kind": "text", + "keyOrLocator": "div", + "text": "x", + "line": 40, + "count": 4, + "notes": "", + "corrected_text": "X", + "actualLine": 40, + "replacementType": "jsx-text" + }, + { + "route": "(main)/user/[userId]", + "file": "src/app/(main)/user/[userId]/components/UserCard.tsx", + "componentOrFn": "UserCard", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "gender", + "line": 182, + "count": 1, + "notes": "", + "corrected_text": "Gender", + "actualLine": 182, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/wallet", + "file": "src/app/(main)/wallet/components/Dashboard/IncomeCard.tsx", + "componentOrFn": "handleWithdraw", + "kind": "dialog", + "keyOrLocator": "alert", + "text": "暂无可提现金额", + "line": 20, + "count": 1, + "notes": "", + "corrected_text": "No amount available for withdrawal", + "actualLine": 20, + "replacementType": "function-arg" + }, + { + "route": "(main)/wallet", + "file": "src/app/(main)/wallet/components/Dashboard/IncomeCard.tsx", + "componentOrFn": "IncomeCard", + "kind": "text", + "keyOrLocator": "div", + "text": "xxx", + "line": 64, + "count": 4, + "notes": "", + "corrected_text": "Xxx", + "actualLine": 64, + "replacementType": "jsx-text" + }, + { + "route": "(main)/wallet/transactions", + "file": "src/app/(main)/wallet/transactions/components/TransactionItem.tsx", + "componentOrFn": "DiamondIcon", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "diamond", + "line": 18, + "count": 1, + "notes": "", + "corrected_text": "Diamond", + "actualLine": 18, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatModelDrawer.tsx", + "componentOrFn": "ChatModelDrawer", + "kind": "text", + "keyOrLocator": "InlineDrawer", + "text": "文本消息价格是指与角色进行文本消息对话的价格,含发送语音,含发送图片,发送礼物。", + "line": 49, + "count": 1, + "notes": "", + "corrected_text": "Text message price refers to the fee for text conversations with the character, including sending voice messages, images, and gifts.", + "actualLine": 49, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatModelDrawer.tsx", + "componentOrFn": "ChatModelDrawer", + "kind": "text", + "keyOrLocator": "InlineDrawer", + "text": "语音通话消息价格是指与角色进行语音电话对话的价格,按条计算", + "line": 50, + "count": 1, + "notes": "", + "corrected_text": "Voice Call Message price refers to the cost of having a voice call conversation with a character. It is charged per message.", + "actualLine": 50, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx", + "componentOrFn": "ChatProfileEditDrawer", + "kind": "placeholder", + "keyOrLocator": "Textarea.placeholder", + "text": "描述你所扮演角色的人物背景、性格特征", + "line": 339, + "count": 1, + "notes": "", + "corrected_text": "Describe the character's background and personality traits.", + "actualLine": 339, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx", + "componentOrFn": "ChatProfileEditDrawer", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "保存编辑内容", + "line": 360, + "count": 1, + "notes": "", + "corrected_text": "Unsaved Edits", + "actualLine": 360, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx", + "componentOrFn": "ChatProfileEditDrawer", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "退出后编辑的内容不会被保存,请确认是否继续退出?", + "line": 362, + "count": 1, + "notes": "", + "corrected_text": "The edited content will not be saved after exiting. Please confirm whether to continue exiting?", + "actualLine": 362, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx", + "componentOrFn": "ChatProfileEditDrawer", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "Confirm", + "line": 370, + "count": 1, + "notes": "", + "corrected_text": "Exit", + "actualLine": 370, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/SendGiftsDrawer.tsx", + "componentOrFn": "handleSendGift", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "送礼物失败,请重试", + "line": 182, + "count": 1, + "notes": "", + "corrected_text": "Gift sending failed. Please try again.", + "actualLine": 195, + "replacementType": "function-arg" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatFirstGuideDialog/index.tsx", + "componentOrFn": "ChatFirstGuideDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "Go to your personal homepage, create an album for your character, attract followers, and increase your income.", + "line": 31, + "count": 1, + "notes": "", + "corrected_text": "Generate images for your character's album to attract fans and earn revenue.", + "actualLine": 31, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatMessageAction/ChatInput.tsx", + "componentOrFn": "handleVoiceRecord", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "语音时间太短", + "line": 392, + "count": 1, + "notes": "", + "corrected_text": "Voice too short", + "actualLine": 394, + "replacementType": "function-arg" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatMessageItems/ChatOtherTextContainer.tsx", + "componentOrFn": "debouncedFeedback", + "kind": "toast", + "keyOrLocator": "toast.error", + "text": "操作失败,请重试", + "line": 87, + "count": 1, + "notes": "", + "corrected_text": "Operation failed, please try again", + "actualLine": 87, + "replacementType": "function-arg" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatMessageItems/ChatUserTextContainer.tsx", + "componentOrFn": "ChatUserTextContainer", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "sending-failed", + "line": 31, + "count": 1, + "notes": "", + "corrected_text": "Sending-failed", + "actualLine": 48, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/CrushLevelAvatar/index.tsx", + "componentOrFn": "CrushLevelAvatar", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "heart pulse 1", + "line": 249, + "count": 1, + "notes": "", + "corrected_text": "Heart pulse 1", + "actualLine": 249, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/CrushLevelAvatar/index.tsx", + "componentOrFn": "CrushLevelAvatar", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "heart pulse 2", + "line": 257, + "count": 1, + "notes": "", + "corrected_text": "Heart pulse 2", + "actualLine": 257, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/DeleteMessageDialog.tsx", + "componentOrFn": "DeleteMessageDialog", + "kind": "text", + "keyOrLocator": "AlertDialog", + "text": "删除内容后无法恢复,但不会改变已经产生的心动值以及角色记忆。请确认是否删除", + "line": 46, + "count": 1, + "notes": "", + "corrected_text": "Deletion is permanent. Your accumulated Affection points and the character's memories will not be affected. Please confirm deletion.", + "actualLine": 46, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/index.tsx", + "componentOrFn": "ChatProfileDrawer", + "kind": "alt", + "keyOrLocator": "Image.alt", + "text": "gender", + "line": 113, + "count": 1, + "notes": "", + "corrected_text": "Gender", + "actualLine": 113, + "replacementType": "jsx-attribute" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/CrushLevelDrawer/index.tsx", + "componentOrFn": "renderLineText", + "kind": "text", + "keyOrLocator": "div", + "text": "暂无心动关系", + "line": 117, + "count": 1, + "notes": "", + "corrected_text": "No heart relationship", + "actualLine": 117, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/CrushLevelDrawer/index.tsx", + "componentOrFn": "CrushLevelDrawer", + "kind": "text", + "keyOrLocator": "InlineDrawer", + "text": "*通过聊天或送礼增加心动值,24小时不联系心动值会自动扣减", + "line": 184, + "count": 1, + "notes": "", + "corrected_text": "* Increase the crush value through chat or gift giving, and the heart value will be automatically deducted if you do not contact for 24 hours.", + "actualLine": 184, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/CrushLevelDrawer/index.tsx", + "componentOrFn": "CrushLevelDrawer", + "kind": "text", + "keyOrLocator": "InlineDrawer", + "text": "*虚拟角色会根据对话的情绪感受,酌情判断增加或者减少心动值", + "line": 185, + "count": 1, + "notes": "", + "corrected_text": "* The virtual character will increase or decrease the crush value according to the emotional feelings of the conversation", + "actualLine": 185, + "replacementType": "jsx-text" + }, + { + "route": "(main)/chat/[aiId]", + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/CrushLevelDrawer/index.tsx", + "componentOrFn": "CrushLevelDrawer", + "kind": "text", + "keyOrLocator": "InlineDrawer", + "text": "*心动值会提升心动等级,通过升级解锁称号,功能,以及不同的角色对话阶段", + "line": 186, + "count": 1, + "notes": "", + "corrected_text": "* The crush value will increase the crush level, and unlock titles, functions, and different character dialogue stages through upgrades", + "actualLine": 186, + "replacementType": "jsx-text" + } + ], + "conflicts": [ + { + "file": "src/app/debug-mock/page.tsx", + "line": 85, + "text": " Mock API 调试页面", + "corrected_text": " Mock API debug page", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/server-device-test/page.tsx", + "line": 17, + "text": "️ 服务端设备ID测试", + "corrected_text": "️ Server level device ID testing", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/server-device-test/page.tsx", + "line": 25, + "text": " 设备ID信息", + "corrected_text": " Device ID information", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/server-device-test/page.tsx", + "line": 60, + "text": " 请求信息", + "corrected_text": " Request information", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/server-device-test/page.tsx", + "line": 90, + "text": " 服务端设备ID处理流程", + "corrected_text": " Server level device ID processing flow", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/test-avatar-crop/page.tsx", + "line": 77, + "text": "选择图片", + "corrected_text": "Select image", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/test-avatar-crop/page.tsx", + "line": 197, + "text": " 设计细节", + "corrected_text": " Design details", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/test-avatar-crop/page.tsx", + "line": 90, + "text": "选择图片", + "corrected_text": "Select image", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/test-discord/page.tsx", + "line": 171, + "text": " 检查环境变量", + "corrected_text": " Checking environment variables", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/test-discord/page.tsx", + "line": 177, + "text": " 真实Discord OAuth登录", + "corrected_text": " Real Discord OAuth Login", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/test-discord/page.tsx", + "line": 181, + "text": " 退出登录", + "corrected_text": " log out", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/test-image-crop/page.tsx", + "line": 70, + "text": "选择图片", + "corrected_text": "Select image", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/test-image-crop/page.tsx", + "line": 83, + "text": "选择图片", + "corrected_text": "Select image", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/test-middleware/page.tsx", + "line": 52, + "text": " Middleware 测试页面", + "corrected_text": " Middleware test page", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/test-s3-upload/page.tsx", + "line": 23, + "text": "import { useS3Upload } from '@/hooks/useS3Upload' import { BizTypeEnum } from '@/services/common/types' const { uploading, progress, error, uploadFile } = useS3Upload({ bizType: BizTypeEnum.Role, maxRetries: 3, retryDelay: 2000, onSuccess: (url) => console.log('上传成功:', url), onError: (error) => console.error('上传失败:', error), onProgress: (progress) => console.log('进度:', progress.percentage + '%') }) // 使用 await uploadFile(file)", + "corrected_text": "Import { useS3Upload } from '@/hooks/useS3Upload' import { BizTypeEnum } from '@/services/common/types' const { uploading, progress, error, uploadFile } = useS3Upload({ bizType: BizTypeEnum.Role, maxRetries: 3, retryDelay: 2000, onSuccess: (url) => console.log('上传成功:', url), onError: (error) => console.error('上传失败:', error), onProgress: (progress) => console.log('进度:', progress.percentage + '%') }) // 使用 await uploadFile(file)", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/components/features/device-info.tsx", + "line": 41, + "text": " 设备信息", + "corrected_text": " Device information", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/components/features/device-info.tsx", + "line": 82, + "text": " 设备ID说明", + "corrected_text": " Device ID Description", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/components/features/genderInput.tsx", + "line": 20, + "text": true, + "corrected_text": "True", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/creator/creator-page.tsx", + "line": 40, + "text": "gift", + "corrected_text": "Gift", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 3 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/test-voice-wave/page.tsx", + "line": 38, + "text": " 整体渐变:", + "corrected_text": " Overall gradual change:", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/test-voice-wave/page.tsx", + "line": 39, + "text": " 涟漪扩散:", + "corrected_text": " Ripple diffusion:", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/test-voice-wave/page.tsx", + "line": 40, + "text": "动画停止时所有声波条统一为短高度", + "corrected_text": "When the animation stops, all sound bars are unified to a short height", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/test-voice-wave/page.tsx", + "line": 41, + "text": " 平滑过渡:", + "corrected_text": " Smooth transition:", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/test-voice-wave/page.tsx", + "line": 123, + "text": "import { VoiceWaveAnimation } from \"@/components/ui/voice-wave-animation\"; // 基础使用 // 自定义声波条数量 // 停止动画时的效果 ", + "corrected_text": "Import { VoiceWaveAnimation } from \"@/components/ui/voice-wave-animation\"; // 基础使用 // 自定义声波条数量 // 停止动画时的效果 ", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/test-voice-wave/page.tsx", + "line": 148, + "text": "动画停止时所有声波条统一为短高度", + "corrected_text": "When the animation stops, all sound bars are unified to a short height", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/vip/vip-page.tsx", + "line": 40, + "text": "Crush Level VIP", + "corrected_text": "CrushLevel VIP", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/vip/vip-page.tsx", + "line": 45, + "text": "Crush Level VIP", + "corrected_text": "CrushLevel VIP", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(auth)/login/components/DiscordButton.tsx", + "line": 34, + "text": "Discord登录失败", + "corrected_text": "Login failed", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 3 个匹配项,需要人工确认" + }, + { + "file": "src/app/(auth)/share/[userId]/share-page.tsx", + "line": 124, + "text": "chat", + "corrected_text": "Chat", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/create/components/ImageForm.tsx", + "line": 45, + "text": "请上传或生成一张图片", + "corrected_text": "Please upload or generate an image", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/create/components/ImageForm.tsx", + "line": 418, + "text": "you have reached the maximum number of AI creations.", + "corrected_text": "You have reached the maximum number of AI creations.", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/generate/components/GenaralImageCard.tsx", + "line": 29, + "text": "image", + "corrected_text": "Image", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 3 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/generate/components/GeneralBuyTimesDialog.tsx", + "line": 106, + "text": "diamond", + "corrected_text": "Diamond", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/generate/components/GeneralImageWithCountButton.tsx", + "line": 60, + "text": "vip", + "corrected_text": "Vip", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/edit/[aiId]/image/image-page.tsx", + "line": 34, + "text": "请上传或生成一张图片", + "corrected_text": "Please upload or generate an image", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/home/components/Meet/MeetCard.tsx", + "line": 172, + "text": true, + "corrected_text": "True", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/user/[userId]/components/AlbumImageViewerAction.tsx", + "line": 111, + "text": "diamond", + "corrected_text": "Diamond", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/wallet/charge/result/result-page.tsx", + "line": 69, + "text": "pending", + "corrected_text": "Pending", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 3 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/wallet/components/Dashboard/IncomeCard.tsx", + "line": 43, + "text": "diamond-icon", + "corrected_text": "Diamond-icon", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 2 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatButtleDrawer.tsx", + "line": 98, + "text": "vip", + "corrected_text": "Vip", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 3 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatModelDrawer.tsx", + "line": 61, + "text": "diamond", + "corrected_text": "Diamond", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 3 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/chat/[aiId]/components/ChatDrawers/CrushLevelRetrieveDrawer.tsx", + "line": 118, + "text": "Crush Level", + "corrected_text": "CrushLevel", + "conflictType": "MULTIPLE_MATCHES", + "conflictReason": "找到 4 个匹配项,需要人工确认" + }, + { + "file": "src/app/(main)/chat/[aiId]/components/ChatMessageAction/ChatInput.tsx", + "line": 109, + "text": "图片上传失败,请重试", + "corrected_text": "Image upload failed, please try again.", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + }, + { + "file": "src/app/(main)/chat/[aiId]/components/ChatMessageAction/ChatInput.tsx", + "line": 303, + "text": "图片处理失败,请重试", + "corrected_text": "Image processing failed, please try again.", + "conflictType": "TEXT_NOT_FOUND_IN_FILE", + "conflictReason": "在文件中找不到匹配的文本" + } + ] +} \ No newline at end of file diff --git a/src/app/(auth)/about/page.tsx b/src/app/(auth)/about/page.tsx new file mode 100644 index 0000000..be7f3a0 --- /dev/null +++ b/src/app/(auth)/about/page.tsx @@ -0,0 +1,45 @@ + + +const AboutPage = () => { + + return ( +
+
+
+
+ Banner +
+ +
+
+ Grow your love story with CrushLevel AI—From "Hi" to "I Do", sparked by every chat +
+ +
+ At CrushLevel AI, every chat writes a new verse in your love epic— +
+
+ From that tentative "Hi" to the trembling "I do", find a home for the flirts you never sent, +
+
+ the responses you longed for, +
+
+ and the risky emotional gambles you feared to take. +
+ +
+ {`Contact Us: ${process.env.NEXT_PUBLIC_EMAIL_CONTACT_US}`} +
+
+
+
+
+ ); +} + +export default AboutPage; \ No newline at end of file diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx new file mode 100644 index 0000000..b3f9ed1 --- /dev/null +++ b/src/app/(auth)/layout.tsx @@ -0,0 +1,13 @@ +import { ReactNode } from "react"; + +const AuthLayout = ({ children }: { children: ReactNode }) => { + return ( +
+
+ {children} +
+
+ ); +} + +export default AuthLayout; \ No newline at end of file diff --git a/src/app/(auth)/login/components/DiscordButton.tsx b/src/app/(auth)/login/components/DiscordButton.tsx new file mode 100644 index 0000000..81a812b --- /dev/null +++ b/src/app/(auth)/login/components/DiscordButton.tsx @@ -0,0 +1,118 @@ +"use client" + +import { discordOAuth } from "@/lib/oauth/discord"; +import { SocialButton } from "./SocialButton"; +import { toast } from "sonner"; +import { useEffect, useRef } from "react"; +import { useLogin } from "@/hooks/auth"; +import { useRouter, useSearchParams } from "next/navigation"; +import { tokenManager } from "@/lib/auth/token"; +import { AppClient, ThirdType } from "@/services/auth"; + +const DiscordButton = () => { + const login = useLogin() + const router = useRouter() + const searchParams = useSearchParams() + const redirect = searchParams.get('redirect'); + + // 处理Discord OAuth回调 + useEffect(() => { + const discordCode = searchParams.get('discord_code') + const discordState = searchParams.get('discord_state') + const error = searchParams.get('error') + + // 处理错误情况 + if (error) { + toast.error("Discord login failed") + + // 清理URL参数 + const newUrl = new URL(window.location.href) + newUrl.searchParams.delete('error') + router.replace(newUrl.pathname) + return + } + + // 处理Discord授权码 + if (discordCode) { + // 验证state参数(可选的安全检查) + const savedState = sessionStorage.getItem('discord_oauth_state') + if (savedState && discordState && savedState !== discordState) { + toast.error("Discord login failed") + return + } + + // 使用code调用后端登录接口 + const deviceId = tokenManager.getDeviceId() + const loginData = { + appClient: AppClient.Web, + deviceCode: deviceId, + thirdToken: discordCode, // 直接传递Discord授权码 + thirdType: ThirdType.Discord + } + + login.mutate(loginData, { + onSuccess: () => { + toast.success("Login successful") + + // 清理URL参数和sessionStorage + sessionStorage.removeItem('discord_oauth_state') + const newUrl = new URL(window.location.href) + newUrl.searchParams.delete('discord_code') + newUrl.searchParams.delete('discord_state') + router.replace(newUrl.pathname) + + const loginRedirectUrl = sessionStorage.getItem('login_redirect_url') + + // 重定向到首页或指定页面 + if (loginRedirectUrl) { + router.push(loginRedirectUrl) + } else { + router.push('/') + } + }, + onError: (error) => { + // 清理URL参数 + const newUrl = new URL(window.location.href) + newUrl.searchParams.delete('discord_code') + newUrl.searchParams.delete('discord_state') + newUrl.searchParams.delete('redirect') + router.replace(newUrl.pathname) + } + }) + } + }, []) + + const handleDiscordLogin = () => { + try { + // 生成随机state用于安全验证 + const state = Math.random().toString(36).substring(2, 15) + + // 获取Discord授权URL + const authUrl = discordOAuth.getAuthUrl(state) + + // 将state保存到sessionStorage用于后续验证 + if (typeof window !== 'undefined') { + sessionStorage.setItem('discord_oauth_state', state) + sessionStorage.setItem('login_redirect_url', redirect || '') + } + + // 跳转到Discord授权页面 + window.location.href = authUrl + } catch (error) { + console.error('Discord login error:', error) + toast.error("Discord login failed") + } + } + + return ( + } + onClick={handleDiscordLogin} + disabled={login.isPending} + > + {login.isPending ? "Signing in..." : "Continue with Discord"} + + ); +} + +export default DiscordButton; \ No newline at end of file diff --git a/src/app/(auth)/login/components/GoogleButton.tsx b/src/app/(auth)/login/components/GoogleButton.tsx new file mode 100644 index 0000000..3789b50 --- /dev/null +++ b/src/app/(auth)/login/components/GoogleButton.tsx @@ -0,0 +1,128 @@ +"use client" + +import { googleOAuth, type GoogleCredentialResponse } from "@/lib/oauth/google"; +import { SocialButton } from "./SocialButton"; +import { toast } from "sonner"; +import { useEffect, useState, useRef } from "react"; +import { useLogin } from "@/hooks/auth"; +import { useRouter, useSearchParams } from "next/navigation"; +import { tokenManager } from "@/lib/auth/token"; +import { AppClient, ThirdType } from "@/services/auth"; + +const GoogleButton = () => { + const login = useLogin() + const router = useRouter() + const searchParams = useSearchParams() + const redirect = searchParams.get('redirect'); + const [isLoading, setIsLoading] = useState(false) + const isInitializedRef = useRef(false) + + // 处理 Google ID Token 回调 + const handleGoogleResponse = async (response: GoogleCredentialResponse) => { + try { + setIsLoading(true) + + // 使用 ID token (JWT) 调用后端登录接口 + const deviceId = tokenManager.getDeviceId() + const loginData = { + appClient: AppClient.Web, + deviceCode: deviceId, + thirdToken: response.credential, // Google ID Token (JWT) + thirdType: ThirdType.Google + } + + login.mutate(loginData, { + onSuccess: () => { + toast.success("Login successful") + + const loginRedirectUrl = sessionStorage.getItem('login_redirect_url') + sessionStorage.removeItem('login_redirect_url') + + // 重定向到首页或指定页面 + if (loginRedirectUrl) { + router.push(loginRedirectUrl) + } else { + router.push('/') + } + }, + onError: (error) => { + console.error('Login error:', error) + toast.error("Login failed") + setIsLoading(false) + } + }) + } catch (error) { + console.error('Google login error:', error) + toast.error("Google login failed") + setIsLoading(false) + } + } + + // 加载 Google Identity Services SDK 并初始化 + useEffect(() => { + const loadAndInitGoogleSDK = async () => { + try { + if (isInitializedRef.current) return + + await googleOAuth.loadScript() + + // 初始化 Google Identity + if (window.google?.accounts?.id) { + googleOAuth.initGoogleId(handleGoogleResponse) + isInitializedRef.current = true + console.log('Google Identity Services initialized') + } + } catch (error) { + console.error('Failed to load Google SDK:', error) + toast.error("Failed to load Google login") + } + } + + loadAndInitGoogleSDK() + }, [handleGoogleResponse]) + + const handleGoogleLogin = async () => { + try { + setIsLoading(true) + + // 保存重定向 URL + if (typeof window !== 'undefined') { + sessionStorage.setItem('login_redirect_url', redirect || '') + } + + // 确保 SDK 已加载并初始化 + if (!window.google?.accounts?.id) { + await googleOAuth.loadScript() + googleOAuth.initGoogleId(handleGoogleResponse) + } + + // 触发 One Tap 登录流程 + if (window.google?.accounts?.id) { + window.google.accounts.id.prompt((notification) => { + if (notification.isNotDisplayed() || notification.isSkippedMoment()) { + console.log('One Tap not displayed:', notification.getNotDisplayedReason() || notification.getSkippedReason()) + // One Tap 无法显示时,使用备选方案 (可选) + setIsLoading(false) + } + }) + } + } catch (error) { + console.error('Google login error:', error) + toast.error("Failed to initialize Google login") + setIsLoading(false) + } + } + + return ( + } + onClick={handleGoogleLogin} + disabled={login.isPending} + > + {login.isPending ? "Signing in..." : "Continue with Google"} + + ); +} + +export default GoogleButton; + diff --git a/src/app/(auth)/login/components/ImageCarousel.tsx b/src/app/(auth)/login/components/ImageCarousel.tsx new file mode 100644 index 0000000..2b28608 --- /dev/null +++ b/src/app/(auth)/login/components/ImageCarousel.tsx @@ -0,0 +1,79 @@ +'use client' + +import Image from "next/image" +import { useState, useEffect } from "react" + +interface ImageCarouselProps { + images: string[] + className?: string + autoPlay?: boolean + interval?: number +} + +export function ImageCarousel({ + images, + className = "", + autoPlay = true, + interval = 3000 +}: ImageCarouselProps) { + const [currentIndex, setCurrentIndex] = useState(0) + const [isTransitioning, setIsTransitioning] = useState(false) + + useEffect(() => { + if (!autoPlay || images.length <= 1) return + + const timer = setInterval(() => { + setIsTransitioning(true) + setTimeout(() => { + setCurrentIndex((prevIndex) => + prevIndex === images.length - 1 ? 0 : prevIndex + 1 + ) + setIsTransitioning(false) + }, 250) // 渐隐时间的一半 + }, interval) + + return () => clearInterval(timer) + }, [autoPlay, interval, images.length]) + + if (images.length === 0) { + return
No picture.
+ } + + return ( +
+ {/* 主图片容器 */} +
+ {images.map((image, index) => ( + {`Slide + ))} +
+ + {/* 指示器圆点 */} + {/*
+ {images.map((_, index) => ( +
*/} +
+ ) +} diff --git a/src/app/(auth)/login/components/LeftPanel.tsx b/src/app/(auth)/login/components/LeftPanel.tsx new file mode 100644 index 0000000..f06dd27 --- /dev/null +++ b/src/app/(auth)/login/components/LeftPanel.tsx @@ -0,0 +1,98 @@ +"use client"; + +import Image from "next/image"; +import { useState, useEffect, useMemo } from "react"; +import { ScrollingBackground } from "./ScrollingBackground"; + +interface LeftPanelProps { + scrollBg: string; + images: string[]; +} + +// 基础文字内容 +const baseTexts = [ + { title: "AI Date", subtitle: "From 'Hi' to 'I Do', sparked by every chat." }, + { title: "Crush", subtitle: "From 'Hi' to 'I Do', sparked by every chat." }, + { title: "Chat", subtitle: "From 'Hi' to 'I Do', sparked by every chat." }, +]; + +// 根据图片数量循环生成文字内容 +const generateImageTexts = (count: number) => { + return Array.from({ length: count }, (_, i) => baseTexts[i % baseTexts.length]); +}; + +export function LeftPanel({ scrollBg, images }: LeftPanelProps) { + const [currentIndex, setCurrentIndex] = useState(0); + const [isTransitioning, setIsTransitioning] = useState(false); + + // 根据图片数量动态生成文案(使用 useMemo 优化性能) + const imageTexts = useMemo(() => generateImageTexts(images.length), [images.length]); + + useEffect(() => { + if (images.length <= 1) return; + + const timer = setInterval(() => { + setIsTransitioning(true); + setTimeout(() => { + setCurrentIndex((prevIndex) => + prevIndex === images.length - 1 ? 0 : prevIndex + 1 + ); + setIsTransitioning(false); + }, 300); + }, 3000); // 每3秒切换一次 + + return () => clearInterval(timer); + }, [images.length]); + + const currentText = imageTexts[currentIndex] || imageTexts[0]; + + return ( +
+ {/* 滚动背景 */} + + + {/* 内容层 */} +
+ {/* 文字内容 - 在图片上方 */} +
+
+

+ {currentText.title} + logo +

+
+

+ {currentText.subtitle} +

+
+ + {/* 角色图片 - 尽可能放大,紧贴底部 */} +
+ {images.map((image, index) => ( +
+ {`Character +
+ ))} +
+
+
+ ); +} + diff --git a/src/app/(auth)/login/components/ScrollingBackground.tsx b/src/app/(auth)/login/components/ScrollingBackground.tsx new file mode 100644 index 0000000..091a746 --- /dev/null +++ b/src/app/(auth)/login/components/ScrollingBackground.tsx @@ -0,0 +1,70 @@ +"use client"; + +import { useEffect, useRef } from "react"; + +interface ScrollingBackgroundProps { + imageSrc: string; +} + +export function ScrollingBackground({ imageSrc }: ScrollingBackgroundProps) { + const containerRef = useRef(null); + + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + let scrollPosition = 0; + const speed = 0.5; // 滚动速度,数字越大滚动越快 + + const animate = () => { + scrollPosition += speed; + + // 当滚动到一半时重置(因为我们有两张图片) + if (scrollPosition >= container.scrollHeight / 2) { + scrollPosition = 0; + } + + container.scrollTop = scrollPosition; + requestAnimationFrame(animate); + }; + + const animationId = requestAnimationFrame(animate); + + return () => { + cancelAnimationFrame(animationId); + }; + }, []); + + return ( +
+ {/* 隐藏滚动条的样式 */} + + + {/* 两张相同的图片,用于无缝循环 */} +
+ Background + Background +
+
+ ); +} + diff --git a/src/app/(auth)/login/components/SocialButton.tsx b/src/app/(auth)/login/components/SocialButton.tsx new file mode 100644 index 0000000..280293b --- /dev/null +++ b/src/app/(auth)/login/components/SocialButton.tsx @@ -0,0 +1,26 @@ +"use client" +import { Button } from "@/components/ui/button" +import type { ReactNode } from "react" + +interface SocialButtonProps { + icon: ReactNode + children: ReactNode + onClick?: () => void + disabled?: boolean + loading?: boolean +} + +export function SocialButton({ icon, children, loading, onClick, disabled }: SocialButtonProps) { + return ( + + ) +} diff --git a/src/app/(auth)/login/components/login-form.tsx b/src/app/(auth)/login/components/login-form.tsx new file mode 100644 index 0000000..4cfe126 --- /dev/null +++ b/src/app/(auth)/login/components/login-form.tsx @@ -0,0 +1,50 @@ +"use client" +import { SocialButton } from "./SocialButton" +import Link from "next/link" +import { toast } from "sonner" +import DiscordButton from "./DiscordButton" +import GoogleButton from "./GoogleButton" + +export function LoginForm() { + + const handleAppleLogin = () => { + toast.info("Apple Sign In", { + description: "Apple登录功能正在开发中..." + }) + } + + return ( +
+
+

Log in/Sign up

+

Chat, Crush, AI Date

+
+ +
+ + + + + } + onClick={handleAppleLogin} + > + Continue with Apple + +
+ +
+

+ By continuing, you agree to CrushLevel's{" "} + + User Agreement + {" "} + and{" "} + + Privacy Policy + +

+
+
+ ) +} diff --git a/src/app/(auth)/login/fields/fields-page.tsx b/src/app/(auth)/login/fields/fields-page.tsx new file mode 100644 index 0000000..f83bede --- /dev/null +++ b/src/app/(auth)/login/fields/fields-page.tsx @@ -0,0 +1,248 @@ +"use client" +import Image from "next/image" +import { useForm } from "react-hook-form" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { Label } from "@/components/ui/label" +import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select" +import { Form, FormField, FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import GenderInput from "@/components/features/genderInput" +import { zodResolver } from "@hookform/resolvers/zod" +import * as z from "zod" +import { Gender } from "@/types/user" +import { useEffect, useState } from "react" +import dayjs from "dayjs" +import { useCheckNickname, useCompleteUser, useCurrentUser } from "@/hooks/auth" +import { useRouter, useSearchParams } from "next/navigation" + +const currentYear = dayjs().year() +const years = Array.from({ length: currentYear - 1950 + 1 }, (_, i) => `${1950 + i}`) +const months = Array.from({ length: 12 }, (_, i) => `${i + 1}`.padStart(2, "0")) +const monthTexts = Array.from({ length: 12 }, (_, i) => dayjs().month(i).format('MMM')) + +function getDaysInMonth(year: string, month: string) { + return Array.from( + { length: dayjs(`${year}-${month}`).daysInMonth() }, + (_, i) => `${i + 1}`.padStart(2, "0") + ) +} + +function calculateAge(year: string, month: string, day: string) { + const birthDate = dayjs(`${year}-${month}-${day}`) + const today = dayjs() + return today.diff(birthDate, 'year') +} + +const schema = z.object({ + nickname: z.string().trim().min(1, "Please Enter nickname").min(2, "Nickname must be between 2 and 20 characters"), + gender: z.enum(Gender, { message: "Please select your gender" }), + year: z.string().min(1, "Please select your birthday"), + month: z.string().min(1, "Please select your birthday"), + day: z.string().min(1, "Please select your birthday") +}).refine((data) => { + const age = calculateAge(data.year, data.month, data.day) + return age >= 18 +}, { + message: "Character age must be at least 18 years old", + path: ["year"] +}) + +type FormValues = z.infer + +export default function FieldsPage() { + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { + nickname: "", + gender: undefined, + year: "2000", + month: "01", + day: "01" + }, + mode: "all", + }, +); + const { mutateAsync } = useCompleteUser(); + const { data: user, refetch } = useCurrentUser(); + const router = useRouter(); + const searchParams = useSearchParams(); + const redirect = searchParams.get("redirect"); + const [loading, setLoading] = useState(false); + + const { mutateAsync: checkNickname } = useCheckNickname({ + onError: (error) => { + form.setError("nickname", { + message: error.errorMsg, + }) + } + }); + + const selectedYear = form.watch("year") + const selectedMonth = form.watch("month") + const days = selectedYear && selectedMonth ? getDaysInMonth(selectedYear, selectedMonth) : [] + + useEffect(() => { + if (!user?.cpUserInfo) { + router.push("/") + } + }, [user?.cpUserInfo]) + + useEffect(() => { + const currentDay = form.getValues("day") + const maxDay = days[days.length - 1] + if (parseInt(currentDay) > parseInt(maxDay)) { + form.setValue("day", maxDay) + } + }, [selectedYear, selectedMonth, days, form]) + + async function onSubmit(data: FormValues) { + if (!user?.userId) { + return; + } + setLoading(true) + try { + const isExist = await checkNickname({ + nickname: data.nickname.trim(), + }) + if (isExist) { + form.setError("nickname", { + message: "This nickname is already taken", + }) + return; + } + await mutateAsync({ + nickname: data.nickname.trim(), + sex: data.gender, + birthday: `${data.year}-${data.month}-${data.day}`, + userId: user?.userId, + }) + await refetch(); + if (redirect) { + router.push(decodeURIComponent(redirect)) + } else { + router.push("/") + } + } catch (error) { + console.error(error) + } finally { + setLoading(false) + } + } + + return ( +
+
+ Anime character +
+
+
+

Personal Information

+
+ + ( + + Nickname + + + + + + )} + /> + + ( + + Gender + + + +
Please note: gender cannot be changed after setting
+ +
+ )} + /> + +
+ +
+ ( + + + + )} + /> + + ( + + + + )} + /> + + ( + + + + )} + /> +
+ + {form.formState.errors.year?.message || form.formState.errors.month?.message || form.formState.errors.day?.message} + +
+ +
+
+ +
+ + +
+
+
+ ) +} diff --git a/src/app/(auth)/login/fields/page.tsx b/src/app/(auth)/login/fields/page.tsx new file mode 100644 index 0000000..c0a1a7a --- /dev/null +++ b/src/app/(auth)/login/fields/page.tsx @@ -0,0 +1,10 @@ +import FieldsPage from "./fields-page"; + +const Page = () => { + + return ( + + ); +} + +export default Page; \ No newline at end of file diff --git a/src/app/(auth)/login/login-page.tsx b/src/app/(auth)/login/login-page.tsx new file mode 100644 index 0000000..9c40946 --- /dev/null +++ b/src/app/(auth)/login/login-page.tsx @@ -0,0 +1,74 @@ +"use client" +import Image from "next/image" +import { LoginForm } from "./components/login-form" +import { LeftPanel } from "./components/LeftPanel" +import { IconButton } from "@/components/ui/button"; +import { useRouter } from "next/navigation"; + +const scrollBg = "/images/login/v1/bg.png"; +const images = [ + "/images/login/v1/1.png", + "/images/login/v1/2.png", + "/images/login/v1/3.png", + "/images/login/v1/4.png", + "/images/login/v1/5.png", + "/images/login/v1/6.png", + "/images/login/v1/7.png", + "/images/login/v1/8.png", + "/images/login/v1/9.png", + "/images/login/v1/10.png", +] + +export default function LoginPage() { + const router = useRouter(); + + const handleClose = () => { + router.replace('/'); + } + + return ( +
+ {/* 左侧 - 滚动背景 + 图片轮播 (桌面端显示) */} +
+ + + {/* 关闭按钮 - 桌面端 */} + +
+ + {/* 右侧 - 登录表单 */} +
+ {/* 关闭按钮 - 移动端 */} + + + {/* Logo */} +
+ Crush Level +
+ + {/* 登录表单 */} +
+ +
+
+
+ ) +} diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx new file mode 100644 index 0000000..7917d99 --- /dev/null +++ b/src/app/(auth)/login/page.tsx @@ -0,0 +1,10 @@ +import LoginPage from "./login-page"; + +const Page = () => { + + return ( + + ); +} + +export default Page; \ No newline at end of file diff --git a/src/app/(auth)/policy/page.tsx b/src/app/(auth)/policy/page.tsx new file mode 100644 index 0000000..4519a35 --- /dev/null +++ b/src/app/(auth)/policy/page.tsx @@ -0,0 +1,8 @@ +import { redirect } from "next/navigation"; + +const Policy = () => { + + redirect("/policy/privacy") +} + +export default Policy; \ No newline at end of file diff --git a/src/app/(auth)/policy/privacy/page.tsx b/src/app/(auth)/policy/privacy/page.tsx new file mode 100644 index 0000000..231d579 --- /dev/null +++ b/src/app/(auth)/policy/privacy/page.tsx @@ -0,0 +1,232 @@ +export default function PrivacyPolicyPage() { + return ( +
+
+
+ {/* 主标题 */} +
+

+ Crushlevel Privacy Policy +

+
+ + {/* 前言 */} +
+

+ Welcome to the Crushlevel application ("this App") and related website (Crushlevel.ai, "this Website"). We recognize the importance of your personal information and are committed to protecting your privacy rights and information security. This Privacy Policy ("Policy") explains the principles and practices we follow when collecting, using, storing, and safeguarding your personal information. Please read this Policy carefully before using our services. By registering or using our services, you acknowledge full acceptance of this Policy. For any inquiries, contact us through the provided channels. +

+
+ + {/* 隐私政策条款 */} +
+ + {/* 1. Scope of Application */} +
+
+

1. Scope of Application

+
+
+

+ This Policy applies to all personal information processing activities (including collection, use, storage, and protection) during your use of this App and Website. We comply with applicable laws in your country/region and international data protection standards. +

+
+
+ + {/* 2. Collection of Personal Information */} +
+
+

2. Collection of Personal Information

+
+ + {/* a) Registration Information */} +
+
+

a) Registration Information

+
+
+

+ When registering an account, we collect your mobile number or email address to create your account, facilitate login, and deliver service notifications. +

+
+
+ + {/* b) Service Usage Information */} +
+
+

b) Service Usage Information

+
+
+

+ Virtual Character Creation: Descriptions you provide for AI characters (excluding non-personal information). +

+

+ Chat Interactions: Text, images, voice messages, and other content exchanged with AI characters to enable service functionality, store chat history, and optimize features. +

+

+ Feature Engagement: Records related to relationship upgrades, unlocked features, and rewards to monitor service usage and ensure performance. +

+
+
+ + {/* c) Payment Information */} +
+
+

c) Payment Information

+
+
+

+ For transactions (pay-per-message, subscriptions, virtual currency purchases), we collect payment methods, amounts, and timestamps to complete transactions and deliver paid services. +

+
+
+ + {/* d) Device & Log Information */} +
+
+

d) Device & Log Information

+
+
+

+ To ensure service stability and security, we may collect device model, OS version, IP address, browser type, timestamps, and access records. +

+
+
+
+ + {/* 3. Use of Personal Information */} +
+
+

3. Use of Personal Information

+
+
+

+ We use your personal information to: +

+
    +
  • Provide services (account management, character creation, chat interactions, paid features, etc.).
  • +
  • Optimize services by analyzing usage patterns to enhance functionality.
  • +
  • Conduct promotional activities (with your consent or where permitted by law), without disclosing sensitive data.
  • +
  • Troubleshoot issues, maintain service integrity, and protect your rights.
  • +
+
+
+ + {/* 4. Storage of Personal Information */} +
+
+

4. Storage of Personal Information

+
+
+

+ a) Your data is stored on secure servers with robust technical/administrative measures to prevent loss, leakage, tampering, or misuse. +

+

+ b) Retention periods align with service needs and legal requirements. Post-expiry, data is deleted or anonymized. +

+

+ c) Data is primarily stored in your country/region. Cross-border transfers (if any) comply with applicable laws and implement safeguards (e.g., standard contractual clauses). +

+
+
+ + {/* 5. Protection of Personal Information */} +
+
+

5. Protection of Personal Information

+
+
+

+ a) We implement strict security protocols, including encryption and access controls, to prevent unauthorized access or disclosure. +

+

+ b) Only authorized personnel bound by confidentiality obligations may access your data. +

+

+ c) In case of a data breach, we will take remedial actions and notify you/regulators as required by law. +

+
+
+ + {/* 6. Sharing, Transfer, and Disclosure */} +
+
+

6. Sharing, Transfer, and Disclosure

+
+
+

+ a) We do not share your data with third parties without your explicit consent, except where mandated by law or to protect public/personal interests. +

+

+ b) We do not transfer your data unless: (i) you consent; or (ii) during corporate restructuring (mergers, acquisitions), where recipients must adhere to this Policy. +

+

+ c) Public disclosure occurs only when legally required or requested by authorities, with efforts to minimize exposure. +

+
+
+ + {/* 7. Your Rights */} +
+
+

7. Your Rights

+
+
+

+ a) Access & Correction: Request to view or correct inaccurate/incomplete data. +

+

+ b) Deletion: Request deletion where legally permissible or upon service termination. +

+

+ c) Consent Withdrawal: Withdraw consent for data processing (note: may affect service functionality). +

+

+ d) Account Deactivation: Deactivate your account; data will be processed per relevant policies. +

+
+
+ + {/* 8. Minor Protection */} +
+
+

8. Minor Protection

+
+
+

+ Users under 18 must obtain parental/guardian consent before using our services. We prioritize minors' data protection. Parents/guardians may contact us to review or delete minors' data improperly collected. We strictly comply with minor protection laws in your jurisdiction. +

+
+
+ + {/* 9. Policy Updates */} +
+
+

9. Policy Updates

+
+
+

+ We may revise this Policy due to legal changes or operational needs. Revised versions will be published on this App/Website and take effect after the notice period. Continued use constitutes acceptance of changes. Revisions will comply with local legal requirements. +

+
+
+ + {/* 10. Contact Us */} +
+
+

10. Contact Us

+
+
+

+ For questions or to exercise your rights, contact us via provided channels. We will respond within a reasonable timeframe. +

+

+ Thank you for trusting Crushlevel. We strive to protect your information security! +

+
+
+
+
+
+
+ ); +} diff --git a/src/app/(auth)/policy/privacy/privacy.md b/src/app/(auth)/policy/privacy/privacy.md new file mode 100644 index 0000000..6f14375 --- /dev/null +++ b/src/app/(auth)/policy/privacy/privacy.md @@ -0,0 +1,95 @@ + +Crushlevel Privacy Policy + +Welcome to the Crushlevel application ("this App") and related website (Crushlevel.ai, "this Website"). We recognize the importance of your personal information and are committed to protecting your privacy rights and information security. This Privacy Policy ("Policy") explains the principles and practices we follow when collecting, using, storing, and safeguarding your personal information. Please read this Policy carefully before using our services. By registering or using our services, you acknowledge full acceptance of this Policy. For any inquiries, contact us through the provided channels. + +1\. Scope of Application + +This Policy applies to all personal information processing activities (including collection, use, storage, and protection) during your use of this App and Website. We comply with applicable laws in your country/region and international data protection standards. + +2\. Collection of Personal Information + +a) Registration Information + +When registering an account, we collect your mobile number or email address to create your account, facilitate login, and deliver service notifications. + +b) Service Usage Information + +Virtual Character Creation: Descriptions you provide for AI characters (excluding non-personal information). + +Chat Interactions: Text, images, voice messages, and other content exchanged with AI characters to enable service functionality, store chat history, and optimize features. + +Feature Engagement: Records related to relationship upgrades, unlocked features, and rewards to monitor service usage and ensure performance. + +c) Payment Information + +For transactions (pay-per-message, subscriptions, virtual currency purchases), we collect payment methods, amounts, and timestamps to complete transactions and deliver paid services. + +d) Device & Log Information + +To ensure service stability and security, we may collect device model, OS version, IP address, browser type, timestamps, and access records. + +3\. Use of Personal Information + +We use your personal information to: + +a) Provide services (account management, character creation, chat interactions, paid features, etc.). + +b) Optimize services by analyzing usage patterns to enhance functionality. + +c) Conduct promotional activities (with your consent or where permitted by law), without disclosing sensitive data. + +d) Troubleshoot issues, maintain service integrity, and protect your rights. + +4\. Storage of Personal Information + +a) Your data is stored on secure servers with robust technical/administrative measures to prevent loss, leakage, tampering, or misuse. + +b) Retention periods align with service needs and legal requirements. Post-expiry, data is deleted or anonymized. + +c) Data is primarily stored in your country/region. Cross-border transfers (if any) comply with applicable laws and implement safeguards (e.g., standard contractual clauses). + +5\. Protection of Personal Information + +a) We implement strict security protocols, including encryption and access controls, to prevent unauthorized access or disclosure. + +b) Only authorized personnel bound by confidentiality obligations may access your data. + +c) In case of a data breach, we will take remedial actions and notify you/regulators as required by law. + +6\. Sharing, Transfer, and Disclosure + +a) We do not share your data with third parties without your explicit consent, except where mandated by law or to protect public/personal interests. + +b) We do not transfer your data unless: (i) you consent; or (ii) during corporate restructuring (mergers, acquisitions), where recipients must adhere to this Policy. + +c) Public disclosure occurs only when legally required or requested by authorities, with efforts to minimize exposure. + +7\. Your Rights + +a) Access & Correction: Request to view or correct inaccurate/incomplete data. + +b) Deletion: Request deletion where legally permissible or upon service termination. + +c) Consent Withdrawal: Withdraw consent for data processing (note: may affect service functionality). + +d) Account Deactivation: Deactivate your account; data will be processed per relevant policies. + +8\. Minor Protection + +Users under 18 must obtain parental/guardian consent before using our services. We prioritize minors’ data protection. Parents/guardians may contact us to review or delete minors’ data improperly collected. We strictly comply with minor protection laws in your jurisdiction. + +9\. Policy Updates + +We may revise this Policy due to legal changes or operational needs. Revised versions will be published on this App/Website and take effect after the notice period. Continued use constitutes acceptance of changes. Revisions will comply with local legal requirements. + +10\. Contact Us + +For questions or to exercise your rights, contact us via provided channels. We will respond within a reasonable timeframe. + +Thank you for trusting Crushlevel. We strive to protect your information security! + +\ +\ + + diff --git a/src/app/(auth)/policy/recharge/Crushlevel Recharge Service Agreement.md b/src/app/(auth)/policy/recharge/Crushlevel Recharge Service Agreement.md new file mode 100644 index 0000000..25ddb55 --- /dev/null +++ b/src/app/(auth)/policy/recharge/Crushlevel Recharge Service Agreement.md @@ -0,0 +1,75 @@ +# **Crushlevel Recharge Service Agreement** +October 2025 +## **Preamble** +Welcome to use the recharge-related services of "Crushlevel"! + +This Recharge Service Agreement (hereinafter referred to as "this Agreement") is entered into between you and the operator of Crushlevel (hereinafter referred to as the "Platform") and/or its affiliates (hereinafter referred to as the "Company"). The Platform shall provide services to you in accordance with the provisions of this Agreement and the operating rules issued from time to time (hereinafter referred to as the "Services"). For the purpose of providing better services to users, you, as the service user (i.e., the account user who places an order to purchase the Platform's virtual currency, hereinafter referred to as "you"), shall carefully read and fully understand this Agreement before starting to use the Services. Among them, clauses that exempt or limit the Platform's liability, dispute resolution methods, jurisdiction and other important contents will be highlighted in **bold** to draw your attention, and you shall focus on reading these parts. If you do not agree to this Agreement, please do not take any further actions (including but not limited to clicking the operation buttons such as purchasing virtual currency, making payments) or use the Services. + +Minors are prohibited from using the recharge services. The Platform hereby kindly reminds that if you are the guardian of a minor, you shall assume guardianship responsibilities for the minor under your guardianship. When the minor uses the relevant products and services of this Platform, you shall enable the youth mode and/or other minor protection tools, supervise and guide the minor to use the relevant products and services correctly, and at the same time strengthen the restriction and management of online payment methods to jointly create a sound environment for the healthy growth of minors. This Agreement also complies with the provisions on the protection of minors in the U.S. Children's Online Privacy Protection Act (COPPA) and the European General Data Protection Regulation (GDPR) to ensure that the rights and interests of minors are not infringed. +## **I. Service Content** +### **1.1 Definition and Purpose of Virtual Currency** +The virtual currency provided by the Platform to you (hereinafter referred to as "Virtual Currency") is a virtual tool limited to relevant consumption within the Crushlevel Platform. It is not a token, legal tender or advance payment certificate, and does not have the circulation and advance payment value of legal tender. After purchasing the Virtual Currency, you may, in accordance with the instructions and guidelines on the relevant pages of the Platform, use it for the following consumption scenarios, including but not limited to: + +- Paid chat with AI virtual characters; +- Unlocking pictures related to AI virtual characters; +- Purchasing "Affection Points" to increase the interaction level with AI virtual characters; +- Recharging for Platform membership to enjoy exclusive membership benefits; +- Sending virtual gifts to AI virtual characters; +- Unlocking more different types of virtual lovers (AI virtual characters). +### **1.2 Restrictions on the Use of Virtual Currency** +After purchasing the Virtual Currency, you may only use it for the consumption scenarios stipulated in Clause 1.1 of this Agreement. You shall not use it beyond the scope of products/services provided by the Company, nor transfer, trade, sell or gift it between different Crushlevel accounts. +### **1.3 Official Purchase Channels** +You shall purchase the Virtual Currency through the official channels designated by the Platform, including but not limited to the Platform's official website, official mobile application (APP) and third-party payment cooperation channels authorized by the Platform. The Platform does not recognize any third-party channels not authorized by the Company (such as unofficial purchasing agents, private transactions, etc.). If you purchase the Virtual Currency through unauthorized channels, the Platform cannot guarantee that such Virtual Currency can be successfully credited to your account. Moreover, such acts may be accompanied by risks such as fraud, money laundering and account theft, causing irreparable losses or damages to you, the Platform and relevant third parties. Therefore, purchasing through unauthorized channels shall be deemed as a violation. The Platform has the right to deduct or clear the Virtual Currency in your account, restrict all or part of the functions of your account, or temporarily or permanently ban your account. You shall bear all losses caused thereby; if your violation of the aforementioned provisions causes losses to the Platform or other third parties, you shall be liable for full compensation. +### **1.4 Fee Collection and Channel Differences** +The fees for your purchase of the Virtual Currency shall be collected by the Company or a cooperating party designated by the Company. The Platform specially reminds you that relevant service providers of different purchase channels (such as third-party payment institutions, app stores, etc.) may charge channel service fees when you make payments in accordance with their own operating strategies. This may result in differences in the amount of fees required to purchase the same amount of Virtual Currency through different channels, or differences in the amount of Virtual Currency that can be purchased with the same amount of fees. The specific details shall be subject to the page display when you purchase the Virtual Currency. Please carefully confirm the relevant page information (including but not limited to price, quantity, service fee description, etc.) and choose the Virtual Currency purchase channel reasonably. +### **1.5 Provisions on Proxy Recharge Services** +The Platform does not provide any proxy recharge services. If you intend to purchase the Virtual Currency for another person's account, you shall confirm the identity and will of the account user by yourself. Any disputes arising from proxy recharge (including but not limited to the account user denying receipt of the Virtual Currency, requesting a refund, etc.) shall be resolved through negotiation between you and the account user. The Platform shall not bear any liability to you or the account user in this regard. +## **II. Rational Consumption** +### **2.1 Advocacy of Rational Consumption** +The Platform advocates rational consumption and spending within one's means. You must purchase and use the Virtual Currency and relevant services reasonably according to your own consumption capacity and actual needs to avoid excessive consumption. When the amount of Virtual Currency you purchase is relatively large or the purchase frequency is abnormal, the Platform has the right to remind you of rational consumption through pop-up prompts, SMS notifications, etc. You shall attach importance to such reminders and make prudent decisions. +### **2.2 Requirements for the Legitimacy of Funds** +The funds you use to purchase the Virtual Currency shall be legally obtained and you shall have the right to use such funds (in compliance with relevant laws, regulations and tax provisions); if you violate the provisions of this Clause, any disputes or controversies arising therefrom (including but not limited to account freezing, tax penalties due to illegal source of funds, etc.) shall be resolved by yourself and you shall bear all legal consequences. If your acts cause losses to the Platform or third parties, you shall also make full compensation. If the Platform discovers (including but not limited to active discovery, receipt of third-party complaints, notifications from regulatory authorities or judicial organs, etc.) that you are suspected of violating the aforementioned provisions, the Platform has the right to deduct or clear the Virtual Currency in your account, restrict all or part of the functions of your account, or even permanently ban your account; at the same time, the Platform has the right to keep relevant information and report to relevant regulatory authorities and judicial organs. +### **2.3 Resistance to Irregular Consumption Behaviors** +The Platform strictly resists behaviors that induce, stimulate or incite users to consume irrationally (including but not limited to excessive recharge, frequent purchase of virtual gifts, etc.) and behaviors that induce or instigate minors to recharge with false identity information. If you discover the aforementioned irregular behaviors, you may report to the Platform through the publicized channels of the Platform (such as the official customer service email, the report entrance in the APP, etc.). The Platform will take disciplinary measures in accordance with laws and regulations (including but not limited to warning the irregular account, restricting the account functions, banning the account, etc.). We look forward to working with you to build a healthy and orderly Platform ecosystem. +## **III. Your Rights and Obligations** +### **3.1 Obligation of Authenticity of Information and Cooperation in Investigations** +The personal information or materials you provide in the process of using the Services (including but not limited to name, email, payment account information, etc.) shall be true, accurate and complete, and shall comply with the requirements of relevant laws and regulations on personal information protection, such as the U.S. Fair Credit Reporting Act (FCRA) and the European General Data Protection Regulation (GDPR). If laws, regulations or regulatory authorities require you to cooperate in investigations, you shall provide relevant materials and assist in the investigations in accordance with the Platform's requirements. +### **3.2 Responsibility for Purchase Operations** +When purchasing the Virtual Currency, you shall carefully select and/or enter key information such as your account information (e.g., account ID, bound email/mobile phone number) and the quantity of Virtual Currency to be purchased. If due to factors such as your own input errors, improper operations, insufficient understanding of the charging method or failure to confirm the purchase information, there are purchase errors such as wrong account, wrong quantity of Virtual Currency, repeated purchases, etc., resulting in your losses or additional expenses, the Platform has the right not to make compensation or indemnification. +### **3.3 Responsibility for Account Safekeeping** +You shall properly keep your Crushlevel account (including account ID, password, bound email/mobile phone number and verification code, etc.) and be responsible for all operation behaviors and consequences under this account. If the Platform is unable to provide the Services or makes errors in providing the Services due to the following circumstances of yours, resulting in your losses, the Platform shall not bear legal liability unless otherwise explicitly required by laws and regulations: + +- Your account becomes invalid, lost, stolen or banned; +- The third-party payment institution account or bank account bound to your account is frozen, sealed up or has other abnormalities, or you use an uncertified account or an account that does not belong to you; +- You disclose your account password to others or allow others to log in and use your account in other ways; +- Other circumstances where you have intent or gross negligence (such as failure to update account security settings in a timely manner, ignoring account abnormal login reminders, etc.). +### **3.4 Obligation of Compliant Use** +You shall use the Services in a legal and compliant manner, and shall not use the Services for any purposes that are illegal or criminal, violate public order and good customs, harm social ethics (in line with the standards of public order and good customs in the United States and Europe), interfere with the normal operation of the Platform or infringe the legitimate rights and interests of third parties. Your use of the Services shall also not violate any documents or other requirements that are binding on you (if any). The Platform specially reminds you not to lend, transfer or provide your account to others for use in other ways, and to reasonably prevent others from committing acts that violate the aforementioned provisions through your account, so as to protect the security of your account and property. +### **3.5 Specifications for Minor Refund Services** +The Platform provides minor consumption refund services in accordance with laws and regulations to protect the legitimate rights and interests of minors and their guardians (in compliance with the provisions on the protection of minor consumption in the U.S. COPPA and the European GDPR); you shall not use this service for illegal purposes or in improper ways, including but not limited to adults pretending to be minors to defraud refunds, inducing minors to consume and then applying for refunds, etc. The aforementioned acts shall constitute a serious violation of this Agreement. After reasonable confirmation, the Platform has the right to refuse the refund and reserve the right to further pursue your legal liability in accordance with the law (including but not limited to reporting to regulatory authorities, filing a lawsuit, etc.). +### **3.6 Provisions on Third-Party Services** +If the use of the Services involves relevant services provided by third parties (such as payment services, third-party login services, etc.), in addition to complying with the provisions of this Agreement, you shall also agree to and comply with the service agreements and relevant rules of such third parties. Under no circumstances shall any disputes arising from such third parties and their provided relevant services (including but not limited to payment failures, account security issues, etc.) be resolved by you and the third party on your own. The Platform shall not bear any liability to you or the third party in this regard. +## **IV. Rights and Obligations of the Platform** +### **4.1 Right to Adjust Service Rules** +Based on factors such as revisions to laws and regulations, requirements of regulatory authorities in the United States and Europe, transaction security guarantees, updates to operating strategies, and changes in market environment, the Platform has the right to set relevant restrictions and reminders on the Virtual Currency services from time to time, including but not limited to restricting the transaction limit and/or transaction frequency of all or part of the users, prohibiting specific users from using the Services, and adding transaction verification steps (such as identity verification, SMS verification, etc.). The Platform will notify you of the aforementioned adjustments through reasonable methods such as APP pop-ups, official website announcements, and email notifications. If you do not agree to the adjustments, you may stop using the Services; if you continue to use the Services, it shall be deemed that you agree to such adjustments. +### **4.2 Right to Risk Monitoring and Account Management** +To ensure transaction security and the stability of the Platform ecosystem, the Platform has the right to monitor your use of the Services (in compliance with relevant laws and regulations on data security and privacy protection in the United States and Europe). For users or accounts that are reasonably identified as high-risk (including but not limited to those suspected of money laundering, fraud, abnormal account login, large-scale purchase of Virtual Currency followed by rapid consumption, etc.), the Platform may take necessary measures to prevent the expansion of risks and protect the property of users and the ecological security of the Platform. Such necessary measures include deducting or clearing the Virtual Currency in your account, restricting all or part of the functions of your account, or temporarily or permanently banning your account. Before taking the aforementioned measures, the Platform will notify you through reasonable methods as much as possible, unless it is impossible to notify due to emergency situations (such as suspected illegal crimes requiring immediate handling). +### **4.3 Right to Correct Errors** +When the Platform discovers errors in the processing of Virtual Currency (including but not limited to errors in the quantity of Virtual Currency issued or deducted) caused by system failures, network problems, human operation errors or any other reasons, whether the error is beneficial to the Platform or you, the Platform has the right to correct the error. In this case, if the actual quantity of Virtual Currency you receive is less than the quantity you should receive, the Platform will make up the difference to your account as soon as possible after confirming the processing error; if the actual quantity of Virtual Currency you receive is more than the quantity you should receive, the Platform has the right to directly deduct the difference from your account without prior notice. If the Virtual Currency in your account is insufficient to offset the difference, the Platform has the right to require you to make up the difference. You shall fulfill this obligation within the reasonable time limit notified by the Platform; otherwise, the Platform has the right to take measures such as restricting account functions and banning the account. +### **4.4 Right to Change, Suspend or Terminate Services** +The Platform has the right to change, interrupt, suspend or terminate the Services based on specific circumstances such as transaction security, operation plans, national laws and regulations or the requirements of regulatory authorities in the United States and Europe. If the Platform decides to change, interrupt, suspend or terminate the Services, it will notify you in advance through reasonable methods such as APP pop-ups, official website announcements, and email notifications (except for emergency situations such as force majeure and sudden system failures where advance notification is impossible), and handle the unused Virtual Currency balance in your account (excluding the membership recharge amount; for the refund rules of membership recharge amount, please refer to Chapter V of this Agreement) in accordance with the provisions of this Agreement. The Platform shall not bear any tort liability to you due to the change, interruption, suspension or termination of the Services for the aforementioned reasons, unless otherwise stipulated by laws and regulations. +## **V. Refund Rules** +### **5.1 Restrictions on Refunds After Consumption of Virtual Currency** +After you use the Virtual Currency for consumption (including but not limited to paid chat, unlocking pictures, purchasing Affection Points, sending virtual gifts, unlocking virtual lovers, etc.), since the Virtual Currency has been converted into the corresponding services or rights provided by the Platform, and the services related to AI virtual characters are instantaneous and irreversible, **the Platform does not provide refund services for this part of the Virtual Currency**. You shall carefully confirm your consumption needs before consumption. +## **VI. Disclaimer** +### **6.1 Provision of Services in Current State and Risk Warning** +You understand and agree that the Services are provided in accordance with the current state achievable under existing technologies and conditions. The Platform will make its best efforts to provide the Services to you and ensure the security and stability of the Services. However, you also know and acknowledge that the Platform cannot foresee and prevent technical and other risks at all times or at all times, including but not limited to service interruptions, delays, errors or data loss caused by force majeure (such as natural disasters, wars, public health emergencies, etc.), network reasons (such as network congestion, hacker attacks, server failures, etc.), third-party service defects (such as failures of third-party payment institutions, changes in app store policies, etc.), revisions to laws and regulations or adjustments to regulatory policies, etc. In the event of such circumstances, the Platform will make its best commercial efforts to improve the situation, but shall not be obligated to bear any legal liability to you or other third parties, unless such losses are caused by the intentional acts or gross negligence of the Platform. +### **6.2 Disclaimer for System Maintenance and Upgrades** +The Platform may conduct downtime maintenance, system upgrades and function adjustments on its own. If you are unable to use the Services normally due to this, the Platform will notify you of the maintenance/upgrade time and the scope of impact in advance through reasonable methods (except for emergency maintenance), and you agree that the Platform shall not bear legal liability for this. Any losses caused by your attempt to use the Services during the maintenance/upgrade period shall be borne by yourself. +### **6.3 Limitation of Liability** +Under no circumstances shall the Platform be liable for any indirect, punitive, incidental or special damages (including but not limited to loss of profits, loss of expected benefits, loss of data, etc.). Moreover, the total liability of the Platform to you, regardless of the cause or manner (including but not limited to breach of contract, tort, etc.), shall not exceed the total amount of fees you actually paid for using the recharge services. +## **VII. Liability for Breach of Contract** +### **7.1 Handling of Your Breach of Contract** +If you violate any provisions of this Agreement (including but not limited to purchasing Virtual Currency through unauthorized channels, using funds from + +(注:文档部分内容可能由 AI 生成) diff --git a/src/app/(auth)/policy/recharge/page.tsx b/src/app/(auth)/policy/recharge/page.tsx new file mode 100644 index 0000000..c031886 --- /dev/null +++ b/src/app/(auth)/policy/recharge/page.tsx @@ -0,0 +1,377 @@ +export default function RechargeAgreementPage() { + return ( +
+
+
+ {/* 主标题 */} +
+

+ Crushlevel Recharge Service Agreement +

+
+ + {/* 日期 */} +
+

October 2025

+
+ + {/* 前言 */} +
+

+ Welcome to use the recharge-related services of "Crushlevel"! +

+

+ This Recharge Service Agreement (hereinafter referred to as "this Agreement") is entered into between you and the operator of Crushlevel (hereinafter referred to as the "Platform") and/or its affiliates (hereinafter referred to as the "Company"). The Platform shall provide services to you in accordance with the provisions of this Agreement and the operating rules issued from time to time (hereinafter referred to as the "Services"). For the purpose of providing better services to users, you, as the service user (i.e., the account user who places an order to purchase the Platform's virtual currency, hereinafter referred to as "you"), shall carefully read and fully understand this Agreement before starting to use the Services. Among them, clauses that exempt or limit the Platform's liability, dispute resolution methods, jurisdiction and other important contents will be highlighted in bold to draw your attention, and you shall focus on reading these parts. If you do not agree to this Agreement, please do not take any further actions (including but not limited to clicking the operation buttons such as purchasing virtual currency, making payments) or use the Services. +

+

+ Minors are prohibited from using the recharge services. The Platform hereby kindly reminds that if you are the guardian of a minor, you shall assume guardianship responsibilities for the minor under your guardianship. When the minor uses the relevant products and services of this Platform, you shall enable the youth mode and/or other minor protection tools, supervise and guide the minor to use the relevant products and services correctly, and at the same time strengthen the restriction and management of online payment methods to jointly create a sound environment for the healthy growth of minors. This Agreement also complies with the provisions on the protection of minors in the U.S. Children's Online Privacy Protection Act (COPPA) and the European General Data Protection Regulation (GDPR) to ensure that the rights and interests of minors are not infringed. +

+
+ + {/* 协议条款 */} +
+ + {/* I. Service Content */} +
+
+

I. Service Content

+
+ + {/* 1.1 Definition and Purpose of Virtual Currency */} +
+
+

1.1 Definition and Purpose of Virtual Currency

+
+
+

+ The virtual currency provided by the Platform to you (hereinafter referred to as "Virtual Currency") is a virtual tool limited to relevant consumption within the Crushlevel Platform. It is not a token, legal tender or advance payment certificate, and does not have the circulation and advance payment value of legal tender. After purchasing the Virtual Currency, you may, in accordance with the instructions and guidelines on the relevant pages of the Platform, use it for the following consumption scenarios, including but not limited to: +

+
    +
  • Paid chat with AI virtual characters;
  • +
  • Unlocking pictures related to AI virtual characters;
  • +
  • Purchasing "Affection Points" to increase the interaction level with AI virtual characters;
  • +
  • Recharging for Platform membership to enjoy exclusive membership benefits;
  • +
  • Sending virtual gifts to AI virtual characters;
  • +
  • Unlocking more different types of virtual lovers (AI virtual characters).
  • +
+
+
+ + {/* 1.2 Restrictions on the Use of Virtual Currency */} +
+
+

1.2 Restrictions on the Use of Virtual Currency

+
+
+

+ After purchasing the Virtual Currency, you may only use it for the consumption scenarios stipulated in Clause 1.1 of this Agreement. You shall not use it beyond the scope of products/services provided by the Company, nor transfer, trade, sell or gift it between different Crushlevel accounts. +

+
+
+ + {/* 1.3 Official Purchase Channels */} +
+
+

1.3 Official Purchase Channels

+
+
+

+ You shall purchase the Virtual Currency through the official channels designated by the Platform, including but not limited to the Platform's official website, official mobile application (APP) and third-party payment cooperation channels authorized by the Platform. The Platform does not recognize any third-party channels not authorized by the Company (such as unofficial purchasing agents, private transactions, etc.). If you purchase the Virtual Currency through unauthorized channels, the Platform cannot guarantee that such Virtual Currency can be successfully credited to your account. Moreover, such acts may be accompanied by risks such as fraud, money laundering and account theft, causing irreparable losses or damages to you, the Platform and relevant third parties. Therefore, purchasing through unauthorized channels shall be deemed as a violation. The Platform has the right to deduct or clear the Virtual Currency in your account, restrict all or part of the functions of your account, or temporarily or permanently ban your account. You shall bear all losses caused thereby; if your violation of the aforementioned provisions causes losses to the Platform or other third parties, you shall be liable for full compensation. +

+
+
+ + {/* 1.4 Fee Collection and Channel Differences */} +
+
+

1.4 Fee Collection and Channel Differences

+
+
+

+ The fees for your purchase of the Virtual Currency shall be collected by the Company or a cooperating party designated by the Company. The Platform specially reminds you that relevant service providers of different purchase channels (such as third-party payment institutions, app stores, etc.) may charge channel service fees when you make payments in accordance with their own operating strategies. This may result in differences in the amount of fees required to purchase the same amount of Virtual Currency through different channels, or differences in the amount of Virtual Currency that can be purchased with the same amount of fees. The specific details shall be subject to the page display when you purchase the Virtual Currency. Please carefully confirm the relevant page information (including but not limited to price, quantity, service fee description, etc.) and choose the Virtual Currency purchase channel reasonably. +

+
+
+ + {/* 1.5 Provisions on Proxy Recharge Services */} +
+
+

1.5 Provisions on Proxy Recharge Services

+
+
+

+ The Platform does not provide any proxy recharge services. If you intend to purchase the Virtual Currency for another person's account, you shall confirm the identity and will of the account user by yourself. Any disputes arising from proxy recharge (including but not limited to the account user denying receipt of the Virtual Currency, requesting a refund, etc.) shall be resolved through negotiation between you and the account user. The Platform shall not bear any liability to you or the account user in this regard. +

+
+
+
+ + {/* II. Rational Consumption */} +
+
+

II. Rational Consumption

+
+ + {/* 2.1 Advocacy of Rational Consumption */} +
+
+

2.1 Advocacy of Rational Consumption

+
+
+

+ The Platform advocates rational consumption and spending within one's means. You must purchase and use the Virtual Currency and relevant services reasonably according to your own consumption capacity and actual needs to avoid excessive consumption. When the amount of Virtual Currency you purchase is relatively large or the purchase frequency is abnormal, the Platform has the right to remind you of rational consumption through pop-up prompts, SMS notifications, etc. You shall attach importance to such reminders and make prudent decisions. +

+
+
+ + {/* 2.2 Requirements for the Legitimacy of Funds */} +
+
+

2.2 Requirements for the Legitimacy of Funds

+
+
+

+ The funds you use to purchase the Virtual Currency shall be legally obtained and you shall have the right to use such funds (in compliance with relevant laws, regulations and tax provisions); if you violate the provisions of this Clause, any disputes or controversies arising therefrom (including but not limited to account freezing, tax penalties due to illegal source of funds, etc.) shall be resolved by yourself and you shall bear all legal consequences. If your acts cause losses to the Platform or third parties, you shall also make full compensation. If the Platform discovers (including but not limited to active discovery, receipt of third-party complaints, notifications from regulatory authorities or judicial organs, etc.) that you are suspected of violating the aforementioned provisions, the Platform has the right to deduct or clear the Virtual Currency in your account, restrict all or part of the functions of your account, or even permanently ban your account; at the same time, the Platform has the right to keep relevant information and report to relevant regulatory authorities and judicial organs. +

+
+
+ + {/* 2.3 Resistance to Irregular Consumption Behaviors */} +
+
+

2.3 Resistance to Irregular Consumption Behaviors

+
+
+

+ The Platform strictly resists behaviors that induce, stimulate or incite users to consume irrationally (including but not limited to excessive recharge, frequent purchase of virtual gifts, etc.) and behaviors that induce or instigate minors to recharge with false identity information. If you discover the aforementioned irregular behaviors, you may report to the Platform through the publicized channels of the Platform (such as the official customer service email, the report entrance in the APP, etc.). The Platform will take disciplinary measures in accordance with laws and regulations (including but not limited to warning the irregular account, restricting the account functions, banning the account, etc.). We look forward to working with you to build a healthy and orderly Platform ecosystem. +

+
+
+
+ + {/* III. Your Rights and Obligations */} +
+
+

III. Your Rights and Obligations

+
+ + {/* 3.1 Obligation of Authenticity of Information and Cooperation in Investigations */} +
+
+

3.1 Obligation of Authenticity of Information and Cooperation in Investigations

+
+
+

+ The personal information or materials you provide in the process of using the Services (including but not limited to name, email, payment account information, etc.) shall be true, accurate and complete, and shall comply with the requirements of relevant laws and regulations on personal information protection, such as the U.S. Fair Credit Reporting Act (FCRA) and the European General Data Protection Regulation (GDPR). If laws, regulations or regulatory authorities require you to cooperate in investigations, you shall provide relevant materials and assist in the investigations in accordance with the Platform's requirements. +

+
+
+ + {/* 3.2 Responsibility for Purchase Operations */} +
+
+

3.2 Responsibility for Purchase Operations

+
+
+

+ When purchasing the Virtual Currency, you shall carefully select and/or enter key information such as your account information (e.g., account ID, bound email/mobile phone number) and the quantity of Virtual Currency to be purchased. If due to factors such as your own input errors, improper operations, insufficient understanding of the charging method or failure to confirm the purchase information, there are purchase errors such as wrong account, wrong quantity of Virtual Currency, repeated purchases, etc., resulting in your losses or additional expenses, the Platform has the right not to make compensation or indemnification. +

+
+
+ + {/* 3.3 Responsibility for Account Safekeeping */} +
+
+

3.3 Responsibility for Account Safekeeping

+
+
+

+ You shall properly keep your Crushlevel account (including account ID, password, bound email/mobile phone number and verification code, etc.) and be responsible for all operation behaviors and consequences under this account. If the Platform is unable to provide the Services or makes errors in providing the Services due to the following circumstances of yours, resulting in your losses, the Platform shall not bear legal liability unless otherwise explicitly required by laws and regulations: +

+
    +
  • Your account becomes invalid, lost, stolen or banned;
  • +
  • The third-party payment institution account or bank account bound to your account is frozen, sealed up or has other abnormalities, or you use an uncertified account or an account that does not belong to you;
  • +
  • You disclose your account password to others or allow others to log in and use your account in other ways;
  • +
  • Other circumstances where you have intent or gross negligence (such as failure to update account security settings in a timely manner, ignoring account abnormal login reminders, etc.).
  • +
+
+
+ + {/* 3.4 Obligation of Compliant Use */} +
+
+

3.4 Obligation of Compliant Use

+
+
+

+ You shall use the Services in a legal and compliant manner, and shall not use the Services for any purposes that are illegal or criminal, violate public order and good customs, harm social ethics (in line with the standards of public order and good customs in the United States and Europe), interfere with the normal operation of the Platform or infringe the legitimate rights and interests of third parties. Your use of the Services shall also not violate any documents or other requirements that are binding on you (if any). The Platform specially reminds you not to lend, transfer or provide your account to others for use in other ways, and to reasonably prevent others from committing acts that violate the aforementioned provisions through your account, so as to protect the security of your account and property. +

+
+
+ + {/* 3.5 Specifications for Minor Refund Services */} +
+
+

3.5 Specifications for Minor Refund Services

+
+
+

+ The Platform provides minor consumption refund services in accordance with laws and regulations to protect the legitimate rights and interests of minors and their guardians (in compliance with the provisions on the protection of minor consumption in the U.S. COPPA and the European GDPR); you shall not use this service for illegal purposes or in improper ways, including but not limited to adults pretending to be minors to defraud refunds, inducing minors to consume and then applying for refunds, etc. The aforementioned acts shall constitute a serious violation of this Agreement. After reasonable confirmation, the Platform has the right to refuse the refund and reserve the right to further pursue your legal liability in accordance with the law (including but not limited to reporting to regulatory authorities, filing a lawsuit, etc.). +

+
+
+ + {/* 3.6 Provisions on Third-Party Services */} +
+
+

3.6 Provisions on Third-Party Services

+
+
+

+ If the use of the Services involves relevant services provided by third parties (such as payment services, third-party login services, etc.), in addition to complying with the provisions of this Agreement, you shall also agree to and comply with the service agreements and relevant rules of such third parties. Under no circumstances shall any disputes arising from such third parties and their provided relevant services (including but not limited to payment failures, account security issues, etc.) be resolved by you and the third party on your own. The Platform shall not bear any liability to you or the third party in this regard. +

+
+
+
+ + {/* IV. Rights and Obligations of the Platform */} +
+
+

IV. Rights and Obligations of the Platform

+
+ + {/* 4.1 Right to Adjust Service Rules */} +
+
+

4.1 Right to Adjust Service Rules

+
+
+

+ Based on factors such as revisions to laws and regulations, requirements of regulatory authorities in the United States and Europe, transaction security guarantees, updates to operating strategies, and changes in market environment, the Platform has the right to set relevant restrictions and reminders on the Virtual Currency services from time to time, including but not limited to restricting the transaction limit and/or transaction frequency of all or part of the users, prohibiting specific users from using the Services, and adding transaction verification steps (such as identity verification, SMS verification, etc.). The Platform will notify you of the aforementioned adjustments through reasonable methods such as APP pop-ups, official website announcements, and email notifications. If you do not agree to the adjustments, you may stop using the Services; if you continue to use the Services, it shall be deemed that you agree to such adjustments. +

+
+
+ + {/* 4.2 Right to Risk Monitoring and Account Management */} +
+
+

4.2 Right to Risk Monitoring and Account Management

+
+
+

+ To ensure transaction security and the stability of the Platform ecosystem, the Platform has the right to monitor your use of the Services (in compliance with relevant laws and regulations on data security and privacy protection in the United States and Europe). For users or accounts that are reasonably identified as high-risk (including but not limited to those suspected of money laundering, fraud, abnormal account login, large-scale purchase of Virtual Currency followed by rapid consumption, etc.), the Platform may take necessary measures to prevent the expansion of risks and protect the property of users and the ecological security of the Platform. Such necessary measures include deducting or clearing the Virtual Currency in your account, restricting all or part of the functions of your account, or temporarily or permanently banning your account. Before taking the aforementioned measures, the Platform will notify you through reasonable methods as much as possible, unless it is impossible to notify due to emergency situations (such as suspected illegal crimes requiring immediate handling). +

+
+
+ + {/* 4.3 Right to Correct Errors */} +
+
+

4.3 Right to Correct Errors

+
+
+

+ When the Platform discovers errors in the processing of Virtual Currency (including but not limited to errors in the quantity of Virtual Currency issued or deducted) caused by system failures, network problems, human operation errors or any other reasons, whether the error is beneficial to the Platform or you, the Platform has the right to correct the error. In this case, if the actual quantity of Virtual Currency you receive is less than the quantity you should receive, the Platform will make up the difference to your account as soon as possible after confirming the processing error; if the actual quantity of Virtual Currency you receive is more than the quantity you should receive, the Platform has the right to directly deduct the difference from your account without prior notice. If the Virtual Currency in your account is insufficient to offset the difference, the Platform has the right to require you to make up the difference. You shall fulfill this obligation within the reasonable time limit notified by the Platform; otherwise, the Platform has the right to take measures such as restricting account functions and banning the account. +

+
+
+ + {/* 4.4 Right to Change, Suspend or Terminate Services */} +
+
+

4.4 Right to Change, Suspend or Terminate Services

+
+
+

+ The Platform has the right to change, interrupt, suspend or terminate the Services based on specific circumstances such as transaction security, operation plans, national laws and regulations or the requirements of regulatory authorities in the United States and Europe. If the Platform decides to change, interrupt, suspend or terminate the Services, it will notify you in advance through reasonable methods such as APP pop-ups, official website announcements, and email notifications (except for emergency situations such as force majeure and sudden system failures where advance notification is impossible), and handle the unused Virtual Currency balance in your account (excluding the membership recharge amount; for the refund rules of membership recharge amount, please refer to Chapter V of this Agreement) in accordance with the provisions of this Agreement. The Platform shall not bear any tort liability to you due to the change, interruption, suspension or termination of the Services for the aforementioned reasons, unless otherwise stipulated by laws and regulations. +

+
+
+
+ + {/* V. Refund Rules */} +
+
+

V. Refund Rules

+
+ + {/* 5.1 Restrictions on Refunds After Consumption of Virtual Currency */} +
+
+

5.1 Restrictions on Refunds After Consumption of Virtual Currency

+
+
+

+ After you use the Virtual Currency for consumption (including but not limited to paid chat, unlocking pictures, purchasing Affection Points, sending virtual gifts, unlocking virtual lovers, etc.), since the Virtual Currency has been converted into the corresponding services or rights provided by the Platform, and the services related to AI virtual characters are instantaneous and irreversible, the Platform does not provide refund services for this part of the Virtual Currency. You shall carefully confirm your consumption needs before consumption. +

+
+
+
+ + {/* VI. Disclaimer */} +
+
+

VI. Disclaimer

+
+ + {/* 6.1 Provision of Services in Current State and Risk Warning */} +
+
+

6.1 Provision of Services in Current State and Risk Warning

+
+
+

+ You understand and agree that the Services are provided in accordance with the current state achievable under existing technologies and conditions. The Platform will make its best efforts to provide the Services to you and ensure the security and stability of the Services. However, you also know and acknowledge that the Platform cannot foresee and prevent technical and other risks at all times or at all times, including but not limited to service interruptions, delays, errors or data loss caused by force majeure (such as natural disasters, wars, public health emergencies, etc.), network reasons (such as network congestion, hacker attacks, server failures, etc.), third-party service defects (such as failures of third-party payment institutions, changes in app store policies, etc.), revisions to laws and regulations or adjustments to regulatory policies, etc. In the event of such circumstances, the Platform will make its best commercial efforts to improve the situation, but shall not be obligated to bear any legal liability to you or other third parties, unless such losses are caused by the intentional acts or gross negligence of the Platform. +

+
+
+ + {/* 6.2 Disclaimer for System Maintenance and Upgrades */} +
+
+

6.2 Disclaimer for System Maintenance and Upgrades

+
+
+

+ The Platform may conduct downtime maintenance, system upgrades and function adjustments on its own. If you are unable to use the Services normally due to this, the Platform will notify you of the maintenance/upgrade time and the scope of impact in advance through reasonable methods (except for emergency maintenance), and you agree that the Platform shall not bear legal liability for this. Any losses caused by your attempt to use the Services during the maintenance/upgrade period shall be borne by yourself. +

+
+
+ + {/* 6.3 Limitation of Liability */} +
+
+

6.3 Limitation of Liability

+
+
+

+ Under no circumstances shall the Platform be liable for any indirect, punitive, incidental or special damages (including but not limited to loss of profits, loss of expected benefits, loss of data, etc.). Moreover, the total liability of the Platform to you, regardless of the cause or manner (including but not limited to breach of contract, tort, etc.), shall not exceed the total amount of fees you actually paid for using the recharge services. +

+
+
+
+ + {/* VII. Liability for Breach of Contract */} +
+
+

VII. Liability for Breach of Contract

+
+ + {/* 7.1 Handling of Your Breach of Contract */} +
+
+

7.1 Handling of Your Breach of Contract

+
+
+

+ If you violate any provisions of this Agreement (including but not limited to purchasing Virtual Currency through unauthorized channels, using funds from illegal sources, etc.), the Platform has the right to take appropriate measures in accordance with the severity of the violation, including but not limited to warning, restricting account functions, temporarily or permanently banning your account, and requiring you to bear corresponding legal liability. If your violation causes losses to the Platform or third parties, you shall be liable for full compensation. +

+
+
+
+
+
+
+
+ ); +} diff --git a/src/app/(auth)/policy/tos/page.tsx b/src/app/(auth)/policy/tos/page.tsx new file mode 100644 index 0000000..fbd88d8 --- /dev/null +++ b/src/app/(auth)/policy/tos/page.tsx @@ -0,0 +1,288 @@ +export default function TermsOfServicePage() { + return ( +
+
+
+ {/* 主标题 */} +
+

+ Crushlevel User Agreement +

+
+ + {/* 前言 */} +
+

+ Welcome to the Crushlevel application (hereinafter referred to as "this App") and related website (Crushlevel.ai, hereinafter referred to as "this Website"). This User Agreement (hereinafter referred to as "this Agreement") is a legally binding agreement between you (hereinafter referred to as the "User") and the operator of Crushlevel (hereinafter referred to as "We," "Us," or "Our") regarding your use of this App and this Website. Before registering for or using this App and this Website, please read this Agreement carefully and understand its contents in full. If you have any questions regarding this Agreement, you should consult Us. If you do not agree to any part of this Agreement, you should immediately cease registration or use of this App and this Website. Once you register for or use this App and this Website, it means that you have fully understood and agreed to all the terms of this Agreement. +

+
+ + {/* 协议条款 */} +
+ + {/* Article 1: User Eligibility */} +
+
+

Article 1: User Eligibility

+
+
+

+ You declare and warrant that at the time of registering an account for this App and this Website, you are at least 18 years old, possess full civil rights capacity and civil capacity for conduct, and are able to independently bear civil liability. If you are under 18 years old, you should read this Agreement under the supervision of your legal guardian and only use this App and this Website with the consent of your legal guardian. +

+

+ You shall ensure that the registration information provided is true, accurate, and complete, and promptly update your registration information to ensure its validity. If, due to registration information provided by you being untrue, inaccurate, incomplete, or not updated in a timely manner, We are unable to provide you with corresponding services or any other losses arise, you shall bear full responsibility. +

+

+ Each user may register only one account. Registering multiple accounts in any form, including but not limited to using different identity information, phone numbers, etc., is strictly prohibited. If We discover that you have registered multiple accounts, We have the right to restrict, freeze, or terminate such accounts without any liability to you. +

+
+
+ + {/* Article 2: Account Management */} +
+
+

Article 2: Account Management

+
+
+

+ You are responsible for the security of your account and password and shall not disclose your account and password to any third party. If your account and password are used illegally by others due to your own reasons, you shall bear all consequences arising therefrom, and We assume no liability. +

+

+ If you discover that your account and password are being used illegally by others or that other security risks exist, you shall immediately notify Us and take corresponding security measures. Upon receiving your notice, We will take reasonable measures based on the actual circumstances, but We assume no responsibility for the outcome of such measures. +

+

+ Without Our prior written consent, you may not transfer, gift, lease, or sell your account to any third party. If you violate this provision, you shall bear all consequences arising therefrom, and We have the right to restrict, freeze, or terminate the relevant account(s). +

+
+
+ + {/* Article 3: Service Content and Usage Norms */} +
+
+

Article 3: Service Content and Usage Norms

+
+ + {/* (1) Service Content */} +
+
+

(1) Service Content

+
+
+

+ You can create AI virtual characters ("Characters") on this App and this Website. Created Characters fall into two categories: Original and Derivative (based on existing fictional works). +

+

+ Other users can chat with the AI virtual Characters you create. Chat methods include text, images, voice, etc. +

+

+ Chatting with Characters allows users to level up their relationship with the Character, unlocking related features and rewards. +

+
+
+ + {/* (2) Usage Norms */} +
+
+

(2) Usage Norms

+
+
+

+ When using this App and this Website, you shall comply with applicable laws and regulations, public order and good morals, and the provisions of this Agreement. You may not use this App and this Website to engage in any illegal or non-compliant activities. +

+

+ The AI virtual Characters you create and the content you publish during chats (including but not limited to text, images, voice, etc.) must not contain the following: +

+
    +
  • Content that violates laws and regulations, such as content endangering national security, undermining ethnic unity, promoting terrorism, extremism, obscenity, pornography, gambling, etc.;
  • +
  • Content that infringes upon the lawful rights and interests of others, such as infringing upon others' portrait rights, reputation rights, privacy rights, intellectual property rights, etc.;
  • +
  • Content that is false, fraudulent, or misleading;
  • +
  • Content that insults, slanders, intimidates, or harasses others;
  • +
  • Other content that violates public order, good morals, or the provisions of this Agreement.
  • +
+

+ You may not use this App and this Website to engage in any form of network attacks, virus dissemination, spam distribution, or other activities that disrupt the normal operation of this App and this Website. +

+

+ You shall respect the lawful rights and interests of other users and must not maliciously harass or attack other users or infringe upon other users' private information. +

+
+
+
+ + {/* Article 4: Intellectual Property Rights */} +
+
+

Article 4: Intellectual Property Rights

+
+
+

+ We own all intellectual property rights in this App and this Website, including but not limited to copyrights, trademarks, patents, trade secrets, etc. All content of this App and this Website, including but not limited to text, images, audio, video, software, programs, interface design, etc., is protected by laws and regulations. +

+

+ The intellectual property rights in the AI virtual Characters you create and the content you publish on this App and this Website belong to you. However, you grant Us a worldwide, royalty-free, non-exclusive, transferable, and sub-licensable license to use such content for the operation, promotion, marketing, and related activities of this App and this Website. +

+

+ When creating Derivative AI virtual Characters, you shall ensure that the Character does not infringe upon the intellectual property rights of the original work. If any dispute arises due to your creation of a Derivative Character infringing upon others' intellectual property rights, you shall bear full responsibility. If losses are caused to Us, you shall compensate Us accordingly. +

+

+ Without Our prior written permission, you may not use, copy, modify, disseminate, or display any intellectual property content of this App and this Website. +

+
+
+ + {/* Article 5: Payments and Transactions */} +
+
+

Article 5: Payments and Transactions

+
+ + {/* (1) Chat Payments */} +
+
+

(1) Chat Payments

+
+
+

+ Users have a daily limit on free chats with virtual Characters. The specific number of free chats is subject to the actual display within this App and this Website. +

+

+ After the free quota is exhausted, users need to pay per message to continue chatting with virtual Characters. Specific payment standards are subject to the actual display within this App and this Website. +

+
+
+ + {/* (2) Creation Payments */} +
+
+

(2) Creation Payments

+
+
+

+ Creators must pay corresponding fees to create Characters or generate derivative AI images. Fees can be paid via membership subscription or virtual currency. +

+

+ The specific content, pricing, and validity period of membership services are subject to the actual display within this App and this Website. After purchasing membership, the member benefits will be effective for the corresponding validity period. +

+

+ Virtual currency is a type of virtual item within this App and this Website, obtainable by users through purchase using fiat currency. The purchase price of virtual currency is subject to the actual display within this App and this Website. Virtual currency may not be exchanged for fiat currency or transferred/gifted to other users. +

+
+
+ + {/* (3) Transaction Rules */} +
+
+

(3) Transaction Rules

+
+
+

+ Before making any payment, users should carefully confirm the payment information, including but not limited to the amount, content, and payment method. Once payment is successfully completed, no refunds will be issued unless otherwise stipulated by applicable law or as mutually agreed upon by both parties. +

+

+ If payment failure or incorrect payment amount occurs due to force majeure such as system failures or network issues, We will handle the situation accordingly upon verification, including but not limited to refunding or supplementing payment. +

+

+ We reserve the right to adjust payment standards, membership service content, virtual currency prices, etc., based on market conditions and business development needs. Adjusted content will be announced on this App and this Website and will become effective after the announcement. +

+
+
+
+ + {/* Article 6: Privacy Protection */} +
+
+

Article 6: Privacy Protection

+
+
+

+ We value user privacy protection and will collect, use, store, and protect your personal information in accordance with the provisions of the "Privacy Policy". The "Privacy Policy" is an integral part of this Agreement and has the same legal effect as this Agreement. +

+

+ You should read the "Privacy Policy" carefully to understand how We process personal information. If you do not agree to any part of the "Privacy Policy," you should immediately stop using this App and this Website. +

+
+
+ + {/* Article 7: Disclaimer */} +
+
+

Article 7: Disclaimer

+
+
+

+ The services of this App and this Website are provided according to the level of technology and conditions currently available. We will make every effort to ensure the stability and security of the services, but We cannot guarantee that services will be uninterrupted, timely, secure, or error-free. We shall not be liable for any service interruption or malfunction caused by force majeure or third-party reasons. +

+

+ Any losses or risks incurred by you during your use of this App and this Website resulting from the use of third-party services or links shall be borne solely by you, and We assume no liability. +

+

+ You shall bear full responsibility for any losses or legal liabilities arising from your violation of the provisions of this Agreement or applicable laws and regulations. If losses are caused to Us or other users, you shall compensate accordingly. +

+

+ We assume no responsibility for the content on this App and this Website. Regarding the AI virtual Characters created by users and the content published, We only provide a platform service and do not assume responsibility for the authenticity, legality, or accuracy of such content. +

+
+
+ + {/* Article 8: Agreement Modification and Termination */} +
+
+

Article 8: Agreement Modification and Termination

+
+
+

+ We reserve the right to modify and update this Agreement based on changes in laws and regulations and business development needs. The modified agreement will be announced on this App and this Website and will become effective after the announcement period. If you object to the modified agreement, you should immediately stop using this App and this Website. If you continue to use this App and this Website, it means you have accepted the modified agreement. +

+

+ If you violate the provisions of this Agreement, We have the right, based on the severity of the violation, to take actions such as warning you, restricting features, freezing, or terminating your account, and reserve the right to pursue your legal liability. +

+

+ You may apply to Us to deregister your account at any time. After account deregistration, you will no longer be able to use the services of this App and this Website, and your relevant information will be processed in accordance with the "Privacy Policy." +

+

+ If due to legal provisions, government requirements, or other force majeure events this App and this Website cannot continue to provide services, We have the right to terminate this Agreement and will notify you within a reasonable period. +

+
+
+ + {/* Article 9: Governing Law and Dispute Resolution */} +
+
+

Article 9: Governing Law and Dispute Resolution

+
+
+

+ The conclusion, validity, interpretation, performance, and dispute resolution of this Agreement shall be governed by the laws of the jurisdiction where the user registered their account. If the laws of the registration jurisdiction contain no relevant provisions, internationally accepted commercial practices shall apply. +

+

+ Any dispute arising from or in connection with this Agreement shall first be resolved through friendly negotiation between the parties. If no settlement is reached through negotiation, either party shall have the right to file a lawsuit with the competent people's court in the location where We are based. +

+
+
+ + {/* Article 10: Miscellaneous */} +
+
+

Article 10: Miscellaneous

+
+
+

+ This Agreement constitutes the entire agreement between you and Us regarding the use of this App and this Website, superseding any prior agreements or understandings, whether oral or written, concerning the subject matter hereof. +

+

+ If any term of this Agreement is found to be invalid or unenforceable, it shall not affect the validity of the remaining terms. +

+

+ We reserve the right of final interpretation of this Agreement. If you have any questions during your use of this App and this Website, you may contact Us through the contact methods provided within this App and this Website. +

+

+ Users are required to carefully read and strictly comply with the above agreement. Thank you for your support and trust in Crushlevel. We hope you enjoy using it! +

+
+
+
+
+
+
+ ); +} diff --git a/src/app/(auth)/policy/tos/tos.md b/src/app/(auth)/policy/tos/tos.md new file mode 100644 index 0000000..047fcac --- /dev/null +++ b/src/app/(auth)/policy/tos/tos.md @@ -0,0 +1,126 @@ +Crushlevel User Agreement + +Welcome to the Crushlevel application (hereinafter referred to as "this App") and related website (Crushlevel.ai, hereinafter referred to as "this Website"). This User Agreement (hereinafter referred to as "this Agreement") is a legally binding agreement between you (hereinafter referred to as the "User") and the operator of Crushlevel (hereinafter referred to as "We," "Us," or "Our") regarding your use of this App and this Website. Before registering for or using this App and this Website, please read this Agreement carefully and understand its contents in full. If you have any questions regarding this Agreement, you should consult Us. If you do not agree to any part of this Agreement, you should immediately cease registration or use of this App and this Website. Once you register for or use this App and this Website, it means that you have fully understood and agreed to all the terms of this Agreement. + +Article 1: User Eligibility + +You declare and warrant that at the time of registering an account for this App and this Website, you are at least 18 years old, possess full civil rights capacity and civil capacity for conduct, and are able to independently bear civil liability. If you are under 18 years old, you should read this Agreement under the supervision of your legal guardian and only use this App and this Website with the consent of your legal guardian. + +You shall ensure that the registration information provided is true, accurate, and complete, and promptly update your registration information to ensure its validity. If, due to registration information provided by you being untrue, inaccurate, incomplete, or not updated in a timely manner, We are unable to provide you with corresponding services or any other losses arise, you shall bear full responsibility. + +Each user may register only one account. Registering multiple accounts in any form, including but not limited to using different identity information, phone numbers, etc., is strictly prohibited. If We discover that you have registered multiple accounts, We have the right to restrict, freeze, or terminate such accounts without any liability to you. + +Article 2: Account Management + +You are responsible for the security of your account and password and shall not disclose your account and password to any third party. If your account and password are used illegally by others due to your own reasons, you shall bear all consequences arising therefrom, and We assume no liability. + +If you discover that your account and password are being used illegally by others or that other security risks exist, you shall immediately notify Us and take corresponding security measures. Upon receiving your notice, We will take reasonable measures based on the actual circumstances, but We assume no responsibility for the outcome of such measures. + +Without Our prior written consent, you may not transfer, gift, lease, or sell your account to any third party. If you violate this provision, you shall bear all consequences arising therefrom, and We have the right to restrict, freeze, or terminate the relevant account(s). + +Article 3: Service Content and Usage Norms + +(1) Service Content + +You can create AI virtual characters ("Characters") on this App and this Website. Created Characters fall into two categories: Original and Derivative (based on existing fictional works). + +Other users can chat with the AI virtual Characters you create. Chat methods include text, images, voice, etc. + +Chatting with Characters allows users to level up their relationship with the Character, unlocking related features and rewards. + +(2) Usage Norms + +When using this App and this Website, you shall comply with applicable laws and regulations, public order and good morals, and the provisions of this Agreement. You may not use this App and this Website to engage in any illegal or non-compliant activities. + +The AI virtual Characters you create and the content you publish during chats (including but not limited to text, images, voice, etc.) must not contain the following: + +(1) Content that violates laws and regulations, such as content endangering national security, undermining ethnic unity, promoting terrorism, extremism, obscenity, pornography, gambling, etc.; + +(2) Content that infringes upon the lawful rights and interests of others, such as infringing upon others' portrait rights, reputation rights, privacy rights, intellectual property rights, etc.; + +(3) Content that is false, fraudulent, or misleading; + +(4) Content that insults, slanders, intimidates, or harasses others; + +(5) Other content that violates public order, good morals, or the provisions of this Agreement. + +You may not use this App and this Website to engage in any form of network attacks, virus dissemination, spam distribution, or other activities that disrupt the normal operation of this App and this Website. + +You shall respect the lawful rights and interests of other users and must not maliciously harass or attack other users or infringe upon other users' private information. + +Article 4: Intellectual Property Rights + +We own all intellectual property rights in this App and this Website, including but not limited to copyrights, trademarks, patents, trade secrets, etc. All content of this App and this Website, including but not limited to text, images, audio, video, software, programs, interface design, etc., is protected by laws and regulations. + +The intellectual property rights in the AI virtual Characters you create and the content you publish on this App and this Website belong to you. However, you grant Us a worldwide, royalty-free, non-exclusive, transferable, and sub-licensable license to use such content for the operation, promotion, marketing, and related activities of this App and this Website. + +When creating Derivative AI virtual Characters, you shall ensure that the Character does not infringe upon the intellectual property rights of the original work. If any dispute arises due to your creation of a Derivative Character infringing upon others' intellectual property rights, you shall bear full responsibility. If losses are caused to Us, you shall compensate Us accordingly. + +Without Our prior written permission, you may not use, copy, modify, disseminate, or display any intellectual property content of this App and this Website. + +Article 5: Payments and Transactions + +(1) Chat Payments + +Users have a daily limit on free chats with virtual Characters. The specific number of free chats is subject to the actual display within this App and this Website. + +After the free quota is exhausted, users need to pay per message to continue chatting with virtual Characters. Specific payment standards are subject to the actual display within this App and this Website. + +(2) Creation Payments + +Creators must pay corresponding fees to create Characters or generate derivative AI images. Fees can be paid via membership subscription or virtual currency. + +The specific content, pricing, and validity period of membership services are subject to the actual display within this App and this Website. After purchasing membership, the member benefits will be effective for the corresponding validity period. + +Virtual currency is a type of virtual item within this App and this Website, obtainable by users through purchase using fiat currency. The purchase price of virtual currency is subject to the actual display within this App and this Website. Virtual currency may not be exchanged for fiat currency or transferred/gifted to other users. + +(3) Transaction Rules + +Before making any payment, users should carefully confirm the payment information, including but not limited to the amount, content, and payment method. Once payment is successfully completed, no refunds will be issued unless otherwise stipulated by applicable law or as mutually agreed upon by both parties. + +If payment failure or incorrect payment amount occurs due to force majeure such as system failures or network issues, We will handle the situation accordingly upon verification, including but not limited to refunding or supplementing payment. + +We reserve the right to adjust payment standards, membership service content, virtual currency prices, etc., based on market conditions and business development needs. Adjusted content will be announced on this App and this Website and will become effective after the announcement. + +Article 6: Privacy Protection + +We value user privacy protection and will collect, use, store, and protect your personal information in accordance with the provisions of the ["Privacy Policy"](insert link here). The "Privacy Policy" is an integral part of this Agreement and has the same legal effect as this Agreement. + +You should read the "Privacy Policy" carefully to understand how We process personal information. If you do not agree to any part of the "Privacy Policy," you should immediately stop using this App and this Website. + +Article 7: Disclaimer + +The services of this App and this Website are provided according to the level of technology and conditions currently available. We will make every effort to ensure the stability and security of the services, but We cannot guarantee that services will be uninterrupted, timely, secure, or error-free. We shall not be liable for any service interruption or malfunction caused by force majeure or third-party reasons. + +Any losses or risks incurred by you during your use of this App and this Website resulting from the use of third-party services or links shall be borne solely by you, and We assume no liability. + +You shall bear full responsibility for any losses or legal liabilities arising from your violation of the provisions of this Agreement or applicable laws and regulations. If losses are caused to Us or other users, you shall compensate accordingly. + +We assume no responsibility for the content on this App and this Website. Regarding the AI virtual Characters created by users and the content published, We only provide a platform service and do not assume responsibility for the authenticity, legality, or accuracy of such content. + +Article 8: Agreement Modification and Termination + +We reserve the right to modify and update this Agreement based on changes in laws and regulations and business development needs. The modified agreement will be announced on this App and this Website and will become effective after the announcement period. If you object to the modified agreement, you should immediately stop using this App and this Website. If you continue to use this App and this Website, it means you have accepted the modified agreement. + +If you violate the provisions of this Agreement, We have the right, based on the severity of the violation, to take actions such as warning you, restricting features, freezing, or terminating your account, and reserve the right to pursue your legal liability. + +You may apply to Us to deregister your account at any time. After account deregistration, you will no longer be able to use the services of this App and this Website, and your relevant information will be processed in accordance with the "Privacy Policy." + +If due to legal provisions, government requirements, or other force majeure events this App and this Website cannot continue to provide services, We have the right to terminate this Agreement and will notify you within a reasonable period. + +Article 9: Governing Law and Dispute Resolution + +The conclusion, validity, interpretation, performance, and dispute resolution of this Agreement shall be governed by the laws of the jurisdiction where the user registered their account. If the laws of the registration jurisdiction contain no relevant provisions, internationally accepted commercial practices shall apply. + +Any dispute arising from or in connection with this Agreement shall first be resolved through friendly negotiation between the parties. If no settlement is reached through negotiation, either party shall have the right to file a lawsuit with the competent people's court in the location where We are based. + +Article 10: Miscellaneous + +This Agreement constitutes the entire agreement between you and Us regarding the use of this App and this Website, superseding any prior agreements or understandings, whether oral or written, concerning the subject matter hereof. + +If any term of this Agreement is found to be invalid or unenforceable, it shall not affect the validity of the remaining terms. + +We reserve the right of final interpretation of this Agreement. If you have any questions during your use of this App and this Website, you may contact Us through the contact methods provided within this App and this Website. + +Users are required to carefully read and strictly comply with the above agreement. Thank you for your support and trust in Crushlevel. We hope you enjoy using it! + diff --git a/src/app/(auth)/share/[userId]/not-found.tsx b/src/app/(auth)/share/[userId]/not-found.tsx new file mode 100644 index 0000000..ce3d19e --- /dev/null +++ b/src/app/(auth)/share/[userId]/not-found.tsx @@ -0,0 +1,19 @@ +import Empty from "@/components/ui/empty"; +import Image from "next/image"; +import Link from "next/link"; + +export default async function NotFound() { + + return ( +
+
+ +
+ Anime character +
+ + +
+
+ ) +} \ No newline at end of file diff --git a/src/app/(auth)/share/[userId]/page.tsx b/src/app/(auth)/share/[userId]/page.tsx new file mode 100644 index 0000000..8de4aea --- /dev/null +++ b/src/app/(auth)/share/[userId]/page.tsx @@ -0,0 +1,41 @@ +import SharePage from "./share-page"; +import { HydrationBoundary } from "@tanstack/react-query"; +import { dehydrate } from "@tanstack/react-query"; +import { QueryClient } from "@tanstack/react-query"; +import { aiUserKeys } from "@/lib/query-keys"; +import { userService } from "@/services/user"; +import { ApiError } from "@/types/api"; +import { notFound } from "next/navigation"; + +const Page = async ({ params }: { params: Promise<{ userId?: string }> }) => { + const { userId } = await params; + + if (!userId) { + notFound(); + } + + const queryClient = new QueryClient(); + + try { + // 预获取用户基本信息 + await queryClient.fetchQuery({ + queryKey: aiUserKeys.baseInfo({ aiId: Number(userId) }), + queryFn: () => userService.getAIUserBaseInfo({ aiId: Number(userId) }), + }); + } catch (error) { + if (error instanceof ApiError && error.errorCode === "10010012") { + notFound(); + } + // 其他错误不影响页面渲染,让客户端处理 + } + + return ( + + + + ); +} + +export default Page; diff --git a/src/app/(auth)/share/[userId]/share-page.tsx b/src/app/(auth)/share/[userId]/share-page.tsx new file mode 100644 index 0000000..238f5ce --- /dev/null +++ b/src/app/(auth)/share/[userId]/share-page.tsx @@ -0,0 +1,138 @@ +"use client" + +import { useParams } from "next/navigation"; +import { useGetAIUserBaseInfo, useGetAIUserStat } from "@/hooks/aiUser"; +import Image from "next/image"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { formatNumberToKMB, openApp } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { Tag } from "@/components/ui/tag"; +import { calculateAudioDuration, formatAudioDuration, parseTextWithBrackets } from "@/utils/textParser"; +import { useGetIMUserInfo, useGetShareUserInfo } from "@/hooks/useIm"; +import React from "react"; + +const SharePage = () => { + const { userId } = useParams(); + + const { data: imUserInfo } = useGetShareUserInfo({ + aiId: userId ? Number(userId) : 0 + }); + const { data: statData } = useGetAIUserStat({ aiId: Number(userId) }); + const { likedNum } = statData || {}; + + + const { backgroundImg, headImg } = imUserInfo || {}; + + // 计算预估的音频时长 + const estimatedDuration = React.useMemo(() => { + return calculateAudioDuration( + imUserInfo?.dialoguePrologue || '', + imUserInfo?.dialogueSpeechRate || 0 + ); + }, [imUserInfo?.dialoguePrologue, imUserInfo?.dialogueSpeechRate]); + + // 格式化时长显示 + const formattedDuration = formatAudioDuration(estimatedDuration); + + const textParts = parseTextWithBrackets(imUserInfo?.dialoguePrologue || ''); + + const handleOpenApp = () => { + openApp(`crushlevel://profile/${userId}`); + } + + return ( +
+
+ Background + +
+
+ +
+
+
+ + + + {imUserInfo?.nickname?.charAt(0) || ''} + + +
+
{imUserInfo?.nickname || ''}
+
{formatNumberToKMB(likedNum || 0)} Likes
+
+
+
+
+
+
+
+
+
+ {imUserInfo?.introduction} +
+
+
+ {imUserInfo?.characterName} + {imUserInfo?.tagName} +
+ +
+
+
+
+ +
+
Content generated by AI
+
+
+
+ {textParts.map((part, index) => ( + + {part.text} + + ))} + +
+
+ +
+ {formattedDuration} +
+
+
+
+
+
+
+ chat +
+ chat +
Chat, Crush, AI Date
+
+
+ +
+
+
+
+ ); +} + +export default SharePage; \ No newline at end of file diff --git a/src/app/(auth)/share/[userId]/test.tsx b/src/app/(auth)/share/[userId]/test.tsx new file mode 100644 index 0000000..bef2921 --- /dev/null +++ b/src/app/(auth)/share/[userId]/test.tsx @@ -0,0 +1,264 @@ +"use client" + +import { useParams } from "next/navigation"; +import { useGetAIUserBaseInfo } from "@/hooks/aiUser"; +import Image from "next/image"; + +// 按钮组件 +interface MobileButtonProps { + showIcon?: boolean; + icon?: React.ReactNode | null; + btnTxt?: string; + showTxt?: boolean; + size?: "Large" | "Medium" | "Small"; + variant?: "Contrast" | "Basic" | "Ghost"; + type?: "Primary" | "Secondary" | "Tertiary" | "Destructive" | "VIP"; + state?: "Default" | "Disabled" | "Pressed"; + onClick?: () => void; +} + +function MobileButton({ + showIcon = true, + icon = null, + btnTxt = "Button", + showTxt = true, + size = "Large", + variant = "Basic", + type = "Primary", + state = "Default", + onClick +}: MobileButtonProps) { + if (size === "Small" && variant === "Contrast" && type === "Tertiary" && state === "Default") { + return ( + + ); + } + + return ( + + ); +} + +const SharePage = () => { + const { userId } = useParams(); + + const { data: userInfo } = useGetAIUserBaseInfo({ + aiId: userId ? Number(userId) : 0 + }); + + const { homeImageUrl } = userInfo || {}; + + const handleChatClick = () => { + // 跳转到应用或下载页面 + window.open('https://crushlevel.com/download', '_blank'); + }; + + return ( +
+ {/* 背景图片 */} +
+
+ Background +
+
+
+ + {/* 内容容器 */} +
+ {/* 头部用户信息 */} +
+
+ {/* 用户头像 */} +
+ {userInfo?.headImg ? ( + {userInfo.nickname + ) : ( +
+ + {userInfo?.nickname?.charAt(0) || 'U'} + +
+ )} +
+ + {/* 用户名和点赞数 */} +
+
+

+ {userInfo?.nickname || 'Loading...'} +

+
+
+

+ 0 likes +

+
+
+
+
+ + {/* 主要内容区域 */} +
+ {/* 消息内容 */} +
+ + {/* AI信息卡片 */} +
+
+ {/* 介绍文本 */} +
+

+ Intro: + {userInfo?.introduction || 'This is an AI character with unique personality and charm. Start chatting to discover more about them!'} +

+
+ + {/* 标签 */} +
+ {userInfo?.tagName ? ( + userInfo.tagName.split(',').slice(0, 2).map((tag: string, index: number) => ( +
+
+ {tag.trim()} +
+
+ )) + ) : ( + <> +
+
Sensual
+
+
+
Romantic
+
+ + )} +
+
+
+ + {/* AI生成内容提示 */} +
+
+
+ Content generated by AI +
+
+
+ + {/* 示例对话消息 */} +
+
+ {/* 语音标签 */} +
+
+ + + +
+
+ 2'' +
+
+ + {/* 消息内容 */} +
+ (Watching her parents toast you respectfully, I feel very sad.) + Are you? +
+
+
+
+
+ + {/* 底部品牌区域 */} +
+
+ {/* App图标 */} +
+ Crushlevel Logo +
+ + {/* 品牌信息 */} +
+
+ Crushlevel +
+
+ Chat. Crush. AI Date +
+
+ + {/* Chat按钮 */} + +
+
+
+
+ ); +} + +export default SharePage; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/chat-page.tsx b/src/app/(main)/chat/[aiId]/chat-page.tsx new file mode 100644 index 0000000..bd7c2e2 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/chat-page.tsx @@ -0,0 +1,70 @@ +'use client'; + +import ChatMessageList from "./components/ChatMessageList"; +import ChatBackground from "./components/ChatBackground"; +import ChatMessageAction from "./components/ChatMessageAction"; +import ChatDrawers from "./components/ChatDrawers"; +import { ChatConfigProvider } from "./context/chatConfig"; +import { DrawerLayerProvider } from "./components/ChatDrawers/InlineDrawer"; +import { IconButton } from "@/components/ui/button"; +import { useSetAtom } from "jotai"; +import { isChatProfileDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import ChatCall from "./components/ChatCall"; +import { Badge } from "@/components/ui/badge"; +import { RED_DOT_KEYS, useRedDot } from "@/hooks/useRedDot"; +import { useS3TokenCache } from "@/hooks/useS3TokenCache"; +import { BizTypeEnum } from "@/services/common/types"; +import ChatFirstGuideDialog from "./components/ChatFirstGuideDialog"; +import CoinInsufficientDialog from "@/components/features/coin-insufficient-dialog"; + +const ChatPage = () => { + const setDrawerState = useSetAtom(isChatProfileDrawerOpenAtom); + const setIsChatProfileDrawerOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + + const { hasRedDot } = useRedDot(); + + // 预加载S3 Token,提升上传速度 + useS3TokenCache({ + preloadBizTypes: [BizTypeEnum.SOUND_PATH], + refreshBeforeExpireMinutes: 5 + }); + + const handleOpenChatProfileDrawer = () => { + setIsChatProfileDrawerOpen(true); + }; + + const isShowRedDot = hasRedDot(RED_DOT_KEYS.CHAT_BACKGROUND) || hasRedDot(RED_DOT_KEYS.CHAT_BUBBLE); + + return ( + +
+
+
+ + + {/* 消息列表区域 */} +
+ + +
+ + {isShowRedDot && } +
+
+
+
+ + + +
+
+ + + + + + + ); +} + +export default ChatPage; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatBackground.tsx b/src/app/(main)/chat/[aiId]/components/ChatBackground.tsx new file mode 100644 index 0000000..ee2972f --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatBackground.tsx @@ -0,0 +1,31 @@ +"use client"; + +import Image from 'next/image'; +import * as React from 'react'; +import { useChatConfig } from '../context/chatConfig'; + +const ChatBackground = () => { + const { aiInfo } = useChatConfig(); + const { backgroundImg } = aiInfo || {}; + + return ( +
+
+ {backgroundImg && Background} + {/*
*/} + {/* todo */} +
+
+
+
+
+
+ ); +} + +export default ChatBackground; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx new file mode 100644 index 0000000..7c40e2d --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx @@ -0,0 +1,867 @@ +"use client" + +import { useCurrentUser } from "@/hooks/auth"; +import { useChatConfig } from "../../context/chatConfig"; +import CrushLevelAvatar from "../CrushLevelAvatar"; +import { useDoRtcOperation, useGetRtcToken, useStartVoiceChat } from "@/hooks/useIm"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { RTCClient } from "./rtc-types"; +import RtcComponent from "./RtcComponent"; +import { AutoPlayFailedEvent, LocalAudioPropertiesInfo, MediaType, onUserJoinedEvent, onUserLeaveEvent, PlayerEvent } from "@byteplus/rtc"; +import { RtcOperation } from "@/services/im"; +import ChatEndButton from "./ChatEndButton"; +import ChatCallStatus from "./ChatCallStatus"; +import { useAtom, useAtomValue, useSetAtom } from "jotai"; +import { hasReceivedAiGreetingAtom, hasStartAICallAtom, isCallAtom, isCoinInsufficientAtom, selectedConversationIdAtom } from "@/atoms/im"; +import { COIN_INSUFFICIENT_ERROR_CODE } from "@/hooks/useWallet"; +import { useNimChat, useNimMsgContext } from "@/context/NimChat/useNimChat"; +import { CustomMessageType } from "@/types/im"; +import { useQueryClient } from "@tanstack/react-query"; +import { walletKeys } from "@/lib/query-keys"; +import { useAudioActivityDetection } from "@/hooks/useAudioActivityDetection"; + +// 字幕数据结构定义 +interface SubtitleData { + text: string; + language: string; + userId: string; + sequence: number; + definite: boolean; + paragraph: boolean; + roundId?: number; +} + +interface SubtitleMessage { + type: string; + data: SubtitleData[]; +} + +export interface SubtitleState { + userSubtitle: string; // 用户字幕 + aiSubtitle?: string; // AI 字幕(显示用) + aiCompleteMessage: string; // AI完整消息拼接 + currentAiSentence: string; // 当前AI句子缓冲 + isUserSpeaking: boolean; + isAiSpeaking: boolean; + isAiThinking: boolean; + hideInterrupt: boolean; +} + +const durationText = ((duration: number) => { + const hours = Math.floor(duration / 3600000); + const minutes = Math.floor((duration % 3600000) / 60000); + const seconds = Math.floor((duration % 60000) / 1000); + + if (hours > 0) { + return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; + } else if (minutes > 0) { + return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; + } else { + return `00:${seconds.toString().padStart(2, '0')}`; + } +}); + +const ChatCallContainer = () => { + const [isConnected, setIsConnected] = useState(false); + const [remoteStreams, setRemoteStreams] = useState<{ + [key: string]: { + playerComp: React.ReactNode; + }; + }>({}); + const [autoPlayFailUser, setAutoPlayFailUser] = useState([]); + const [isUserMicSpeaking, setisUserMicSpeaking] = useState(false); + const autoPlayFailUserdRef = useRef([]); + const rtc = useRef(null); + const rtcComponentRef = useRef(null); // 添加这个ref来存储RtcComponent的引用 + const playStatus = useRef<{ [key: string]: { audio: boolean; video: boolean } }>({}); + const isJoiningRef = useRef(false); + const callStartTimeRef = useRef(null); // 通话开始时间 + const rtcTimingRef = useRef<{ + rtcJoinStart?: number; + rtcJoinEnd?: number; + localStreamStart?: number; + localStreamEnd?: number; + startApiCall?: number; + startApiSuccess?: number; + aiGreetingReceived?: number; + }>({}); // RTC各阶段时间记录 + const setHasReceivedAiGreeting = useSetAtom(hasReceivedAiGreetingAtom); + const lastCallbackMessageRef = useRef(''); + const { nim } = useNimChat(); + const { sendMessageActive } = useNimMsgContext(); + const selectedConversationId = useAtomValue(selectedConversationIdAtom); + const queryClient = useQueryClient(); + + // 字幕状态管理 - 使用 ref 来同步保存状态,避免异步setState问题 + const subtitleStateRef = useRef({ + userSubtitle: '', + aiSubtitle: '', + aiCompleteMessage: '', + currentAiSentence: '', + isUserSpeaking: false, + isAiSpeaking: false, + isAiThinking: false, + hideInterrupt: false, + }); + + // 用于触发页面更新的状态 + const [subtitleState, setSubtitleState] = useState(subtitleStateRef.current); + + // 音频播放相关 + const connectingAudioRef = useRef(null); + + // 用于跟踪最新的sequence,确保按序处理 + const lastSequenceRef = useRef<{ [userId: string]: number }>({}); + // AI说话结束的防抖定时器 + const aiSpeechEndTimerRef = useRef(null); + // 用户麦克风说话状态延迟定时器 + const userMicSpeakingTimerRef = useRef(null); + + // 本地音频活动检测 + const audioActivityDetection = useAudioActivityDetection({ + threshold: 35, // 音频阈值,可以根据需要调整 + sampleRate: 50, // 50ms 采样率,更快的响应 + }); + const setHasStartAICall = useSetAtom(hasStartAICallAtom); + // 用于存储当前的音频流,以便传递给音频检测 + const currentAudioStreamRef = useRef(null); + const { aiId } = useChatConfig(); + const { data: user } = useCurrentUser(); + const userId = user?.userId; + const roomId = `${user?.userId}-${aiId}`; + const [isCall, setIsCall] = useAtom(isCallAtom); + const setIsCoinInsufficient = useSetAtom(isCoinInsufficientAtom); + + + // 使用 ref 来存储当前通话的 taskId,确保每次开始新通话时生成新的 ID + const taskIdRef = useRef(null); + + // 用于管理请求取消的 AbortController + const abortControllerRef = useRef(null); + + // 生成新的 taskId 的函数 + const generateNewTaskId = useCallback(() => { + const newTaskId = `${roomId}-${new Date().getTime()}`; + taskIdRef.current = newTaskId; + return newTaskId; + }, [roomId]); + + const { data } = useGetRtcToken({ roomId }); + const { mutateAsync: doRtcOperation } = useDoRtcOperation(); + const { mutate: startVoiceChat } = useStartVoiceChat(); + const hasReceivedAiGreeting = useAtomValue(hasReceivedAiGreetingAtom); + + // AI说话结束的防抖处理函数 + const handleAiSpeechEndDebounced = useCallback(() => { + // 清除之前的定时器 + if (aiSpeechEndTimerRef.current) { + clearTimeout(aiSpeechEndTimerRef.current); + } + + // 设置新的防抖定时器 + aiSpeechEndTimerRef.current = setTimeout(() => { + console.log('AI说话结束防抖触发,启用用户麦克风'); + + // 更新状态 + const currentState = subtitleStateRef.current; + const newState = { + ...currentState, + hideInterrupt: true, + }; + + // 启用用户麦克风 + rtc.current?.changeAudioState(true); + + // 更新状态 + updateSubtitleState(newState); + }, 300); // 200毫秒防抖延迟 + }, []); + + // 清理防抖定时器的函数 + const clearAiSpeechEndTimer = useCallback(() => { + if (aiSpeechEndTimerRef.current) { + clearTimeout(aiSpeechEndTimerRef.current); + aiSpeechEndTimerRef.current = null; + } + }, []); + + // 打断功能 + const handleInterrupt = useCallback(async () => { + if (!taskIdRef.current) return; + + // 清除AI说话结束的防抖定时器 + clearAiSpeechEndTimer(); + + // 用户打断时重新启用麦克风 + console.log('用户打断AI,重新启用麦克风'); + rtc.current?.changeAudioState(true); + + await doRtcOperation({ + data: { + roomId, + optType: RtcOperation.INTERRUPT, + aiId, + taskId: taskIdRef.current, + } + }); + setSubtitleState({ + userSubtitle: '', + // aiSubtitle: '', + aiCompleteMessage: '', + currentAiSentence: '', + isUserSpeaking: false, + isAiSpeaking: false, + isAiThinking: false, + hideInterrupt: true, + }); + }, [doRtcOperation, roomId, aiId]); + + const rtcToken = data?.token; + + const handleUserPublishStream = useCallback( + (stream: { userId: string; mediaType: MediaType }) => { + const userId = stream.userId; + if (stream.mediaType & MediaType.VIDEO) { + if (remoteStreams[userId]) { + rtc.current?.setRemoteVideoPlayer(userId, `remoteStream_${userId}`); + } + } + }, + [remoteStreams] + ); + + const handleUserUnpublishStream = (event: { userId: string; mediaType: MediaType }) => { + const { userId, mediaType } = event; + if (mediaType & MediaType.VIDEO) { + rtc.current?.setRemoteVideoPlayer(userId, undefined); + } + }; + + const handleUserJoin = (e: onUserJoinedEvent) => { + console.log('handleUserJoin', e); + }; + + const handleUserLeave = (e: onUserLeaveEvent) => { + const { userInfo } = e; + const remoteUserId = userInfo.userId; + if (remoteStreams[remoteUserId]) { + delete remoteStreams[remoteUserId]; + } + setRemoteStreams({ + ...remoteStreams, + }); + }; + + const addFailUser = (userId: string) => { + const index = autoPlayFailUser.findIndex((item) => item === userId); + if (index === -1) { + autoPlayFailUser.push(userId); + } + setAutoPlayFailUser([...autoPlayFailUser]); + }; + + + const handleAutoPlayFail = (event: AutoPlayFailedEvent) => { + console.log('handleAutoPlayFail', event.userId, event); + const { userId, kind } = event; + + let playUser = playStatus.current?.[userId] || {}; + playUser = { ...playUser, [kind]: false }; + playStatus.current[userId] = playUser; + + addFailUser(userId); + }; + + const handleEventError = (e: any, VERTC: any) => { + if (e.errorCode === VERTC.ErrorCode.DUPLICATE_LOGIN) { + // message.error('你的账号被其他人顶下线了'); + leaveRoom(); + setIsCall(false); + } + }; + + + const playerFail = (params: { type: 'audio' | 'video'; userId: string }) => { + const { type, userId } = params; + let playUser = playStatus.current?.[userId] || {}; + playUser = { ...playUser, [type]: false }; + const { audio, video } = playUser; + if (audio === false || video === false) { + addFailUser(userId); + } + }; + + const handlePlayerEvent = (event: PlayerEvent) => { + const { userId, rawEvent, type } = event; + let playUser = playStatus.current?.[userId] || {}; + if (!playStatus.current) return; + if (rawEvent.type === 'playing') { + playUser = { ...playUser, [type]: true }; + const { audio, video } = playUser; + if (audio !== false && video !== false) { + const _autoPlayFailUser = autoPlayFailUserdRef.current.filter((item) => item !== userId); + setAutoPlayFailUser([..._autoPlayFailUser]); + } + } else if (rawEvent.type === 'pause') { + playerFail({ userId, type }); + } + + playStatus.current[userId] = playUser; + }; + + // 解包二进制数据并验证 + const unpackSubtitleMessage = (message: ArrayBuffer): string | null => { + const kSubtitleHeaderSize = 8; + if (message.byteLength < kSubtitleHeaderSize) { + console.warn('Message size too small'); + return null; + } + + const uint8Array = new Uint8Array(message); + + // 验证魔数 "subv" (0x73756276) + const magicNumber = + (uint8Array[0] << 24) | + (uint8Array[1] << 16) | + (uint8Array[2] << 8) | + uint8Array[3]; + + if (magicNumber !== 0x73756276) { + console.warn('Invalid magic number, not a subtitle message'); + return null; + } + + // 获取内容长度 + const length = + (uint8Array[4] << 24) | + (uint8Array[5] << 16) | + (uint8Array[6] << 8) | + uint8Array[7]; + + if (message.byteLength - kSubtitleHeaderSize !== length) { + console.warn('Message length mismatch'); + return null; + } + + if (length === 0) { + return ''; + } + + // 提取字幕内容 + const subtitleBytes = uint8Array.slice(kSubtitleHeaderSize); + return new TextDecoder('utf-8').decode(subtitleBytes); + }; + + // 解析字幕数据 + const parseSubtitleData = (jsonString: string): SubtitleMessage | null => { + try { + const parsed = JSON.parse(jsonString) as SubtitleMessage; + + if (parsed.type !== 'subtitle' || !Array.isArray(parsed.data)) { + console.warn('Invalid subtitle message format'); + return null; + } + + return parsed; + } catch (error) { + console.error('Failed to parse subtitle JSON:', error); + return null; + } + }; + + // 更新状态的辅助函数 + const updateSubtitleState = (newState: SubtitleState) => { + if (newState.isAiThinking) { + setTimeout(() => { + setSubtitleState({ ...newState }); + // rtc.current?.changeAudioState(false); + }, 200); + return; + } + subtitleStateRef.current = newState; + setSubtitleState({ ...newState }); + }; + + // 结合本地音频检测和服务器回调的用户说话状态 + // const combinedUserSpeaking = subtitleState.isUserSpeaking || audioActivityDetection.isSpeaking; + const combinedUserSpeaking = isUserMicSpeaking; + + // 处理字幕消息 + const handleSubtitleMessage = (subtitleMessage: SubtitleMessage) => { + subtitleMessage.data.forEach((subtitle) => { + console.log('Subtitle:', { + text: subtitle.text, + language: subtitle.language, + userId: subtitle.userId, + sequence: subtitle.sequence, + isComplete: subtitle.definite, + isParagraphEnd: subtitle.paragraph, + roundId: subtitle.roundId + }); + + const isCurrentUser = subtitle.userId === String(`${userId}`); + const userKey = isCurrentUser ? 'user' : 'ai'; + + // 检查sequence序列,确保按序处理 + const lastSequence = lastSequenceRef.current[userKey] || 0; + if (subtitle.sequence <= lastSequence) { + console.log('跳过过期的字幕消息:', subtitle.sequence, '当前最新:', lastSequence); + return; + } + + // 更新sequence + lastSequenceRef.current[userKey] = subtitle.sequence; + + // 获取当前状态(从ref中获取最新状态) + const currentState = subtitleStateRef.current; + const newState = { ...currentState }; + + if (isCurrentUser) { + // 处理用户字幕 + if (subtitle.paragraph === false && subtitle.definite === false) { + // 用户实时说话 + console.log('用户实时字幕:', subtitle.text); + newState.userSubtitle = subtitle.text; + newState.isUserSpeaking = true; + // 用户开始说话时清空AI字幕和完整消息,准备接收新的AI回复 + // newState.aiSubtitle = ''; + newState.aiCompleteMessage = ''; + newState.currentAiSentence = ''; + newState.isAiSpeaking = false; // 现在明确设置为false,切换到listening状态 + newState.isAiThinking = false; + } else if (subtitle.paragraph === true) { + // 用户说话结束 + console.log('用户说话结束:', subtitle.text); + newState.isUserSpeaking = false; + newState.isAiThinking = true; + } + } else { + // 处理AI字幕 + if (subtitle.paragraph === false && subtitle.definite === false) { + // AI实时字幕 - 显示当前句子的实时内容 + console.log('AI实时字幕:', subtitle.text); + + // AI开始说话时清除防抖定时器 + clearAiSpeechEndTimer(); + + newState.currentAiSentence = subtitle.text; + newState.aiSubtitle = newState.aiCompleteMessage + subtitle.text; + + // AI开始说话时禁用用户麦克风 + // if (!currentState.isAiSpeaking) { + // console.log('AI1开始说话,禁用用户麦克风'); + // rtc.current?.changeAudioState(false); + // } + rtc.current?.changeAudioState(false); + newState.isAiSpeaking = true; + newState.isAiThinking = false; + newState.userSubtitle = ''; + newState.isUserSpeaking = false; + newState.hideInterrupt = false; + } else if (subtitle.paragraph === false && subtitle.definite === true) { + // AI确定字幕 - 将确定的句子添加到完整消息中 + console.log('AI确定字幕:', subtitle.text); + // 防重复处理 + if (lastCallbackMessageRef.current === subtitle.text) { + return; + } + lastCallbackMessageRef.current = subtitle.text; + + // 将确定的文本添加到完整消息中 + newState.aiCompleteMessage = newState.aiCompleteMessage + subtitle.text; + newState.currentAiSentence = ''; // 清空当前句子缓冲 + newState.aiSubtitle = newState.aiCompleteMessage; // 更新显示 + newState.isAiSpeaking = true; + newState.isAiThinking = false; + newState.hideInterrupt = false; + } else if (subtitle.paragraph === true) { + // AI段落结束 - 完整回复结束 + console.log('AI回复完成:', subtitle.text); + // 如果还有剩余文本,添加到完整消息中 + if (subtitle.text && !newState.aiCompleteMessage.includes(subtitle.text)) { + newState.aiCompleteMessage = newState.aiCompleteMessage + subtitle.text; + newState.aiSubtitle = newState.aiCompleteMessage; + } + newState.currentAiSentence = ''; + + // 不立即设置 hideInterrupt 和 changeAudioState,而是使用防抖处理 + // newState.hideInterrupt = true; + // rtc.current?.changeAudioState(true); + + // AI说完话后,保持显示状态,不立即切换到listening + // newState.isAiSpeaking = false; // 注释掉这行,让AI字幕继续显示 + newState.isAiThinking = false; + + // 输出完整的AI回复用于调试 + console.log('AI完整回复:', newState.aiCompleteMessage); + + // 使用防抖处理AI说话结束 + handleAiSpeechEndDebounced(); + } + } + + // 更新状态并触发页面刷新 + updateSubtitleState(newState); + + // 判断字幕来源 + if (isCurrentUser) { + console.log('Human user subtitle:', subtitle.text); + } else { + console.log('AI agent subtitle:', subtitle.text); + } + }); + }; + + const handleRoomBinaryMessageReceived = (event: { userId: string; message: ArrayBuffer }) => { + console.log('handleRoomBinaryMessageReceived', event); + const { message } = event; + + // 记录收到AI开场白时间 + if (!rtcTimingRef.current.aiGreetingReceived) { + rtcTimingRef.current.aiGreetingReceived = Date.now(); + const totalDuration = rtcTimingRef.current.aiGreetingReceived - rtcTimingRef.current.rtcJoinStart!; + const startToGreetingDuration = rtcTimingRef.current.aiGreetingReceived - rtcTimingRef.current.startApiSuccess!; + console.log('🎉 收到AI开场白:', new Date().toLocaleString()); + console.log('📊 RTC完整流程耗时统计:'); + console.log(` - RTC加入房间: ${rtcTimingRef.current.rtcJoinEnd! - rtcTimingRef.current.rtcJoinStart!}ms`); + console.log(` - 创建本地流: ${rtcTimingRef.current.localStreamEnd! - rtcTimingRef.current.localStreamStart!}ms`); + console.log(` - START接口调用: ${rtcTimingRef.current.startApiSuccess! - rtcTimingRef.current.startApiCall!}ms`); + console.log(` - START接口到AI开场白: ${startToGreetingDuration}ms`); + console.log(` - 总耗时: ${totalDuration}ms`); + } + + setHasReceivedAiGreeting(true); + if (connectingAudioRef.current) { + connectingAudioRef.current.pause(); + connectingAudioRef.current.currentTime = 0; + connectingAudioRef.current = null; + } + + // 尝试解包字幕消息 + const subtitleJson = unpackSubtitleMessage(message); + + if (subtitleJson !== null) { + // 是字幕消息,进行解析 + const subtitleMessage = parseSubtitleData(subtitleJson); + + if (subtitleMessage) { + handleSubtitleMessage(subtitleMessage); + return; + } + } + } + + const handleLocalAudioPropertiesReport = (event: LocalAudioPropertiesInfo[]) => { + console.log('handleLocalAudioPropertiesReport', event); + // 如果用户麦克风正在说话,则设置isUserMicSpeaking为true + if (event.some((item) => item.audioPropertiesInfo.linearVolume > 25)) { + setisUserMicSpeaking(true); + } else { + setisUserMicSpeaking(false); + } + } + + const leaveRoom = useCallback( + async () => { + if (!rtc.current) return; + // off the event + rtc.current.removeEventListener(); + + try { + await rtc.current.leave(); + } catch (error) { + console.log('leaveRoom error', error); + } + + setAutoPlayFailUser([]); + setSubtitleState({ + userSubtitle: '', + aiSubtitle: '', + aiCompleteMessage: '', + currentAiSentence: '', + isUserSpeaking: false, + isAiSpeaking: false, + isAiThinking: false, + hideInterrupt: false, + }); + // 重置通话开始时间和AI开场白状态 + callStartTimeRef.current = null; + setHasReceivedAiGreeting(false); + isJoiningRef.current = false; + rtcComponentRef.current = null; // 清理RtcComponent引用 + taskIdRef.current = null; // 清理taskId,为下次通话做准备 + + // 重置RTC时间记录 + rtcTimingRef.current = {}; + + // 清理 AbortController + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + abortControllerRef.current = null; + } + + // 清理连接音频 + if (connectingAudioRef.current) { + connectingAudioRef.current.pause(); + connectingAudioRef.current.currentTime = 0; + connectingAudioRef.current = null; + } + + // 停止本地音频活动检测 + audioActivityDetection.stopDetection(); + + // 清理音频流引用 + if (currentAudioStreamRef.current) { + currentAudioStreamRef.current.getTracks().forEach(track => track.stop()); + currentAudioStreamRef.current = null; + } + + // 清理用户麦克风说话状态定时器 + if (userMicSpeakingTimerRef.current) { + clearTimeout(userMicSpeakingTimerRef.current); + userMicSpeakingTimerRef.current = null; + } + + setIsConnected(false); + }, + [setHasReceivedAiGreeting] + ); + + useEffect(() => { + (async () => { + if (!roomId || !userId || !rtc.current || !rtcToken || isJoiningRef.current) return; + + // 在每次开始新通话时生成新的 taskId + const currentTaskId = generateNewTaskId(); + isJoiningRef.current = true; + + // 重置AI开场白状态 + setHasReceivedAiGreeting(false); + + // 记录RTC加入开始时间 + rtcTimingRef.current.rtcJoinStart = Date.now(); + console.log('🚀 RTC加入房间开始:', new Date().toLocaleString()); + + try { + await rtc.current.join((rtcToken as any) || null, roomId, `${userId}`); + + // 记录RTC加入成功时间 + rtcTimingRef.current.rtcJoinEnd = Date.now(); + const rtcJoinDuration = rtcTimingRef.current.rtcJoinEnd - rtcTimingRef.current.rtcJoinStart!; + console.log('✅ RTC加入房间成功:', new Date().toLocaleString(), `耗时: ${rtcJoinDuration}ms`); + + // 记录通话开始时间 + callStartTimeRef.current = Date.now(); + + rtc?.current?.createLocalStream(`${userId}`, async (res: any) => { + // 记录创建本地流开始时间 + rtcTimingRef.current.localStreamStart = Date.now(); + console.log('🎤 创建本地流开始:', new Date().toLocaleString()); + + // rtc.current?.changeAudioState(true); + + // 启动本地音频活动检测 + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + currentAudioStreamRef.current = stream; + audioActivityDetection.startDetection(stream); + console.log('本地音频活动检测已启动'); + } catch (error) { + console.error('启动本地音频检测失败:', error); + } + + try { + // 记录创建本地流结束时间 + rtcTimingRef.current.localStreamEnd = Date.now(); + const localStreamDuration = rtcTimingRef.current.localStreamEnd - rtcTimingRef.current.localStreamStart!; + console.log('✅ 创建本地流完成:', new Date().toLocaleString(), `耗时: ${localStreamDuration}ms`); + + // 创建新的 AbortController 用于这次请求 + const abortController = new AbortController(); + abortControllerRef.current = abortController; + + // 记录START接口调用开始时间 + rtcTimingRef.current.startApiCall = Date.now(); + console.log('📞 调用START接口开始:', new Date().toLocaleString()); + + await doRtcOperation({ + data: { + roomId, + optType: RtcOperation.START, + aiId, + taskId: currentTaskId, + }, + signal: abortController.signal + }); + + await rtc.current?.enableAudioPropertiesReport({ + interval: 300, + }); + + // 记录START接口调用成功时间 + rtcTimingRef.current.startApiSuccess = Date.now(); + const startApiDuration = rtcTimingRef.current.startApiSuccess - rtcTimingRef.current.startApiCall!; + console.log('✅ START接口调用成功:', new Date().toLocaleString(), `耗时: ${startApiDuration}ms`); + + setHasStartAICall(true); + // 请求成功完成,清理 AbortController + abortControllerRef.current = null; + } catch (error: any) { + // 清理 AbortController + abortControllerRef.current = null; + + // 如果是用户主动取消的请求,不需要处理错误 + if (error.name === 'AbortError' || error.name === 'CanceledError') { + console.log('START 请求已被用户取消'); + return; + } + + if (error.errorCode && error.errorCode === COIN_INSUFFICIENT_ERROR_CODE) { + setIsCall(false); + leaveRoom(); + setIsCoinInsufficient(true); + } + } + + // startVoiceChat({ + // roomId, + // aiId, + // taskId: currentTaskId, + // userId, + // }); + setIsConnected(true); + }); + } catch (error: any) { + console.log('join error', error); + isJoiningRef.current = false; + leaveRoom(); + setIsCall(false); + } + })(); + }, [roomId, userId, rtcToken, doRtcOperation, aiId, generateNewTaskId, leaveRoom, setIsCall, setHasReceivedAiGreeting, setHasStartAICall]); + + useEffect(() => { + if (!isCall) return; + const handleCallCoinInsufficient = () => { + queryClient.invalidateQueries({ queryKey: walletKeys.getWalletBalance() }) + leaveRoom(); + setIsCall(false); + setIsCoinInsufficient(true); + } + window.addEventListener('call-coin-insufficient', handleCallCoinInsufficient); + return () => { + window.removeEventListener('call-coin-insufficient', handleCallCoinInsufficient); + } + }, [isCall]); + + // 监听 isCall 变化,当用户关闭通话时取消正在进行的请求 + useEffect(() => { + if (!isCall && abortControllerRef.current) { + console.log('用户关闭通话,取消正在进行的 START 请求'); + abortControllerRef.current.abort(); + abortControllerRef.current = null; + } + }, [isCall]); + + // 控制连接音频播放 + useEffect(() => { + if (!hasReceivedAiGreeting && isCall) { + // 创建或获取音频元素 + if (!connectingAudioRef.current) { + connectingAudioRef.current = new Audio('/voice/connecting.mp3'); + connectingAudioRef.current.loop = true; + } + + // 播放音频 + connectingAudioRef.current.play().catch(error => { + console.log('音频播放失败:', error); + }); + } else { + // 停止音频播放 + if (connectingAudioRef.current) { + connectingAudioRef.current.pause(); + connectingAudioRef.current.currentTime = 0; + } + } + + return () => { + if (connectingAudioRef.current) { + connectingAudioRef.current.pause(); + connectingAudioRef.current.currentTime = 0; + connectingAudioRef.current = null; + } + } + }, [hasReceivedAiGreeting, isCall]); + + // 组件卸载时清理防抖定时器 + useEffect(() => { + return () => { + clearAiSpeechEndTimer(); + // 清理用户麦克风说话状态定时器 + if (userMicSpeakingTimerRef.current) { + clearTimeout(userMicSpeakingTimerRef.current); + userMicSpeakingTimerRef.current = null; + } + }; + }, [clearAiSpeechEndTimer]); + + return ( + <> +
+
+
+
+
+
+ +
+ + + + +
+
+
+ {!rtcComponentRef.current && ( + { + rtc.current = ref; + rtcComponentRef.current = ref; + }} + config={{ + appId: process.env.NEXT_PUBLIC_RTC_APP_ID, + roomId, + }} + streamOptions={{ + audio: true, + video: false, + }} + handleUserPublishStream={handleUserPublishStream} + handleUserUnpublishStream={handleUserUnpublishStream} + handleUserJoin={handleUserJoin} + handleUserLeave={handleUserLeave} + handleAutoPlayFail={handleAutoPlayFail} + handleEventError={handleEventError} + handlePlayerEvent={handlePlayerEvent} + handleRoomBinaryMessageReceived={handleRoomBinaryMessageReceived} + handleLocalAudioPropertiesReport={handleLocalAudioPropertiesReport} + /> + )} + + ); +} + +export default ChatCallContainer; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallStatus.tsx b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallStatus.tsx new file mode 100644 index 0000000..a25f394 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallStatus.tsx @@ -0,0 +1,142 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { SubtitleState } from "./ChatCallContainer"; +import { useAtomValue } from "jotai"; +import { hasReceivedAiGreetingAtom } from "@/atoms/im"; +import { VoiceWaveAnimation } from "@/components/ui/voice-wave-animation"; + +const ChatCallStatus = ({ + isConnected, + subtitleState, + onInterrupt, +}: { + isConnected: boolean; + subtitleState: SubtitleState; + onInterrupt?: () => void; +}) => { + const hasReceivedAiGreeting = useAtomValue(hasReceivedAiGreetingAtom); + + const renderAction = () => { + if (!subtitleState.hideInterrupt) { + return ( + + ) + } + + + if (subtitleState.isAiThinking) { + return ( +
+ {/* 三个圆点动画 */} +
+
+
+
+
+
+ Thinking... +
+
+ ) + } + + + return ( +
+ +
+ Listening... +
+
+ ); + } + + if (!hasReceivedAiGreeting) { + return ( +
+ {/* 三个圆点动画 */} +
+
+
+
+
+
+ Waiting to be connected +
+
+ ); + } + + return ( +
+
{subtitleState.aiSubtitle}
+ {renderAction()} +
+ ) + + + // if (subtitleState.isAiSpeaking) { + // return ( + //
+ // {/* AI字幕显示区域 */} + //
+ //
+ //

+ // {/* 解析字幕文本,分离内心想法和实际对话 */} + // {(() => { + // const subtitle = subtitleState.aiSubtitle; + // const thoughtMatch = subtitle?.match(/^[\((](.*?)[\))]\s*(.*)$/); + + // if (thoughtMatch) { + // const [, thought, speech] = thoughtMatch; + // return ( + // <> + // + // ({thought}) + // + // + // {speech} + // + // + // ); + // } else { + // return ( + // + // {subtitle} + // + // ); + // } + // })()} + //

+ //
+ //
+ + // {/* 打断按钮 */} + // {!subtitleState.hideInterrupt && } + //
+ // ) + // } + + // if (subtitleState.isAiThinking) { + // return ( + //
+ // Thinking + // . + // . + // . + //
+ // ) + // } + + // return ( + //
+ // Listening + // . + // . + // . + //
+ // ) +} + +export default ChatCallStatus; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/ChatEndButton.tsx b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatEndButton.tsx new file mode 100644 index 0000000..7eee599 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatEndButton.tsx @@ -0,0 +1,108 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { useDoRtcOperation } from "@/hooks/useIm"; +import { useChatConfig } from "../../context/chatConfig"; +import { RtcOperation } from "@/services/im"; +import React, { useState } from "react"; +import { useNimChat, useNimMsgContext } from "@/context/NimChat/useNimChat"; +import { CustomMessageType } from "@/types/im"; +import { useAtom, useAtomValue, useSetAtom } from "jotai"; +import { hasReceivedAiGreetingAtom, hasStartAICallAtom, isCallAtom, selectedConversationIdAtom } from "@/atoms/im"; +import { useQueryClient } from "@tanstack/react-query"; +import { walletKeys } from "@/lib/query-keys"; + +const ChatEndButton = ({ + roomId, + taskId, + onLeave, + callStartTime, + abortController, +}: { + roomId: string; + taskId: string; + onLeave: () => Promise; + callStartTime: number | null; + abortController: AbortController | null; +}) => { + const { aiId, handleUserMessage } = useChatConfig(); + const { mutateAsync: doRtcOperation } = useDoRtcOperation(); + const [loading, setLoading] = useState(false); + const { nim } = useNimChat(); + const { sendMessageActive } = useNimMsgContext(); + const selectedConversationId = useAtomValue(selectedConversationIdAtom); + const hasReceivedAiGreeting = useAtomValue(hasReceivedAiGreetingAtom); + const setIsCall = useSetAtom(isCallAtom); + const [hasStartAICall, setHasStartAICall] = useAtom(hasStartAICallAtom); + const queryClient = useQueryClient(); + + const handleEndCall = async () => { + try { + setLoading(true); + const duration = Date.now() - (callStartTime || 0); + // 如果已开始AI通话,则停止AI通话 + try { + if (hasStartAICall) { + await doRtcOperation({ + data: { + roomId: roomId, + optType: RtcOperation.STOP, + aiId: aiId, + taskId: taskId, + duration, + } + }); + } else { + await doRtcOperation({ + data: { + roomId: roomId, + optType: RtcOperation.CANCEL, + aiId: aiId, + taskId: taskId, + duration, + } + }); + } + setHasStartAICall(false); + } catch (error) { + setHasStartAICall(false); + } + + await onLeave(); + + + if (!hasReceivedAiGreeting) { + const text = 'Call Canceled'; + const msg = nim.V2NIMMessageCreator.createCustomMessage( + text, + JSON.stringify({ + type: CustomMessageType.CALL_CANCEL, + duration: Date.now() - (callStartTime || 0), + }) + ); + sendMessageActive({ + msg, + conversationId: selectedConversationId || '', + isLoading: false, + }); + + // 通知用户发送了消息,重置自动聊天定时器 + handleUserMessage(); + } + setIsCall(false); + await queryClient.invalidateQueries({ queryKey: walletKeys.getWalletBalance() }); + } catch (error) { + console.log(error); + } finally { + setLoading(false); + } + } + + return ( + + ); +} + +export default ChatEndButton; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/RtcComponent.tsx b/src/app/(main)/chat/[aiId]/components/ChatCall/RtcComponent.tsx new file mode 100644 index 0000000..f62baa8 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/RtcComponent.tsx @@ -0,0 +1,35 @@ +"use client" +import React from "react"; +import RtcClient from "./rtc-client"; +import { LocalAudioPropertiesInfo } from "@byteplus/rtc"; + +interface IProps { + onRef: (ref: any) => void; + config: any; + streamOptions: any; + handleUserPublishStream: any; + handleUserUnpublishStream: any; + handleUserStartVideoCapture?: any; + handleUserStopVideoCapture?: any; + handleUserJoin: any; + handleUserLeave: any; + handleAutoPlayFail: any; + handleEventError: any; + handlePlayerEvent: any; + handleRoomBinaryMessageReceived: any; + handleLocalAudioPropertiesReport: (event: LocalAudioPropertiesInfo[]) => void; +} + +export default class RtcComponent extends React.Component { + rtc: RtcClient; + constructor(props: IProps) { + super(props); + this.rtc = new RtcClient(props); + } + componentDidMount() { + this.props.onRef(this.rtc); + } + render() { + return <>; + } +} diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/hooks/useCallInterrupt.ts b/src/app/(main)/chat/[aiId]/components/ChatCall/hooks/useCallInterrupt.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/hooks/useCallInterrupt.ts @@ -0,0 +1 @@ + diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/index.tsx b/src/app/(main)/chat/[aiId]/components/ChatCall/index.tsx new file mode 100644 index 0000000..0b113f8 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/index.tsx @@ -0,0 +1,15 @@ +"use client"; + +import { isCallAtom } from "@/atoms/im"; +import ChatCallContainer from "./ChatCallContainer"; +import { useAtomValue } from "jotai"; + +const ChatCall = () => { + const isCall = useAtomValue(isCallAtom); + + if (!isCall) return null; + + return ; +} + +export default ChatCall; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/rtc-client.js b/src/app/(main)/chat/[aiId]/components/ChatCall/rtc-client.js new file mode 100644 index 0000000..27a50a8 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/rtc-client.js @@ -0,0 +1,164 @@ +import VERTC, { MediaType, RoomMode, StreamIndex } from '@byteplus/rtc'; + +export default class RtcClient { + constructor(props) { + this.config = props.config; + this.streamOptions = props.streamOptions; + this.engine = VERTC.createEngine(props.config.appId); + this.handleUserPublishStream = props.handleUserPublishStream; + this.handleUserUnpublishStream = props.handleUserUnpublishStream; + // this.handleUserStartVideoCapture = props.handleUserStartVideoCapture; + // this.handleUserStopVideoCapture = props.handleUserStopVideoCapture; + this.handleEventError = props.handleEventError; + this.setRemoteVideoPlayer = this.setRemoteVideoPlayer.bind(this); + this.handleUserJoin = props.handleUserJoin; + this.handleUserLeave = props.handleUserLeave; + this.handleAutoPlayFail = props.handleAutoPlayFail; + this.handlePlayerEvent = props.handlePlayerEvent; + this.handleRoomBinaryMessageReceived = props.handleRoomBinaryMessageReceived; + this.handleLocalAudioPropertiesReport = props.handleLocalAudioPropertiesReport; + this.bindEngineEvents(); + } + SDKVERSION = VERTC.getSdkVersion(); + bindEngineEvents() { + this.engine.on(VERTC.events.onUserPublishStream, this.handleUserPublishStream); + this.engine.on(VERTC.events.onUserUnpublishStream, this.handleUserUnpublishStream); + // this.engine.on(VERTC.events.onUserStartVideoCapture, this.handleUserStartVideoCapture); + // this.engine.on(VERTC.events.onUserStopVideoCapture, this.handleUserStopVideoCapture); + + this.engine.on(VERTC.events.onUserJoined, this.handleUserJoin); + this.engine.on(VERTC.events.onUserLeave, this.handleUserLeave); + this.engine.on(VERTC.events.onAutoplayFailed, (events) => { + console.log('VERTC.events.onAutoplayFailed', events.userId); + this.handleAutoPlayFail(events); + }); + this.engine.on(VERTC.events.onPlayerEvent, this.handlePlayerEvent); + this.engine.on(VERTC.events.onError, (e) => this.handleEventError(e, VERTC)); + this.engine.on(VERTC.events.onRoomBinaryMessageReceived, this.handleRoomBinaryMessageReceived); + this.engine.on(VERTC.events.onLocalAudioPropertiesReport, this.handleLocalAudioPropertiesReport); + } + async setRemoteVideoPlayer(remoteUserId, domId) { + await this.engine.subscribeStream(remoteUserId, MediaType.AUDIO_AND_VIDEO); + await this.engine.setRemoteVideoPlayer(StreamIndex.STREAM_INDEX_MAIN, { + userId: remoteUserId, + renderDom: domId, + }); + } + /** + * remove the listeners when `createEngine` + */ + removeEventListener() { + this.engine.off(VERTC.events.onUserPublishStream, this.handleStreamAdd); + this.engine.off(VERTC.events.onUserUnpublishStream, this.handleStreamRemove); + // this.engine.off(VERTC.events.onUserStartVideoCapture, this.handleUserStartVideoCapture); + // this.engine.off(VERTC.events.onUserStopVideoCapture, this.handleUserStopVideoCapture); + this.engine.off(VERTC.events.onUserJoined, this.handleUserJoin); + this.engine.off(VERTC.events.onUserLeave, this.handleUserLeave); + this.engine.off(VERTC.events.onAutoplayFailed, this.handleAutoPlayFail); + this.engine.off(VERTC.events.onPlayerEvent, this.handlePlayerEvent); + this.engine.off(VERTC.events.onError, this.handleEventError); + } + join(token, roomId, uid) { + return this.engine.joinRoom( + token, + roomId, + { + userId: uid, + }, + { + isAutoPublish: false, + isAutoSubscribeAudio: true, + isAutoSubscribeVideo: false, + roomMode: RoomMode.RTC, + } + ); + } + /** + * get the devices + * @returns + */ + async getDevices() { + const devices = await VERTC.enumerateAudioCaptureDevices(); + + return { + audioInputs: devices, + }; + } + /** + * create the local stream with the config and publish the local stream + * @param {*} callback + */ + async createLocalStream(userId, callback) { + const devices = await this.getDevices(); + const devicesStatus = { + video: 1, + audio: 1, + }; + if (!devices.audioInputs.length && !devices.videoInputs.length) { + callback({ + code: -1, + msg: 'Failed to enumerate devices.', + devicesStatus: { + video: 0, + audio: 0, + }, + }); + return; + } + if (this.streamOptions.audio && devices.audioInputs.length) { + await this.engine.startAudioCapture(devices.audioInputs[0].deviceId); + } else { + devicesStatus['video'] = 0; + // this.engine.unpublishStream(MediaType.AUDIO); + } + if (this.streamOptions.video && devices.videoInputs.length) { + // await this.engine.startVideoCapture(devices.videoInputs[0].deviceId); + } else { + devicesStatus['audio'] = 0; + // this.engine.unpublishStream(MediaType.VIDEO); + } + // this.engine.setLocalVideoPlayer(StreamIndex.STREAM_INDEX_MAIN, { + // renderDom: 'local-player', + // userId, + // }); + + // this.engine.publishStream(MediaType.AUDIO); + + callback && + callback({ + code: 0, + msg: 'Failed to enumerate devices.', + devicesStatus, + }); + } + + async changeAudioState(isMicOn) { + if (isMicOn) { + await this.engine.publishStream(MediaType.AUDIO); + } else { + await this.engine.unpublishStream(MediaType.AUDIO); + } + } + + // async changeVideoState(isVideoOn) { + // if (isVideoOn) { + // await this.engine.startVideoCapture(); + // } else { + // await this.engine.stopVideoCapture(); + // } + // } + + async leave() { + await Promise.all([ + this.engine?.stopAudioCapture(), + ]); + await this.engine?.unpublishStream(MediaType.AUDIO).catch(console.warn); + this.engine.leaveRoom(); + this.engine.destroy(); + } + + async enableAudioPropertiesReport(config) { + console.log('enableAudioPropertiesReport', config); + await this.engine.enableAudioPropertiesReport(config); + } +} diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/rtc-types.ts b/src/app/(main)/chat/[aiId]/components/ChatCall/rtc-types.ts new file mode 100644 index 0000000..297d529 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/rtc-types.ts @@ -0,0 +1,78 @@ +import { IRTCEngine } from '@byteplus/rtc'; + +export interface AudioStats { + CodecType: string; + End2EndDelay: number; + MuteState: boolean; + PacketLossRate: number; + RecvBitrate: number; + RecvLevel: number; + TotalFreezeTime: number; + TotalPlayDuration: number; + TransportDelay: number; +} + +export interface RTCClient { + engine: IRTCEngine; + init: (...args: any[]) => void; + join: (...args: any[]) => any; + publishStream: (...args: any[]) => Promise; + unpublishStream: (...args: any[]) => Promise; + subscribe: (...args: any[]) => void; + leave: (...args: any[]) => Promise; + on: (...args: any[]) => void; + off: (...args: any[]) => void; + setupLocalVideoPlayer: (...args: any[]) => void; + createLocalStream: (...args: any[]) => void; + setRemoteVideoPlayer: (...args: any[]) => void; + removeEventListener: (...args: any[]) => void; + changeAudioState: (...args: any[]) => void; + changeVideoState: (...args: any[]) => void; + bindEngineEvents: (...args: any[]) => void; + enableAudioPropertiesReport: (...args: any[]) => void; +} + +export interface Stream { + userId: string; + hasAudio: boolean; + hasVideo: boolean; + isScreen: boolean; + videoStreamDescriptions: any[]; + stream: { + screen: boolean; + }; + getId: () => string; + enableAudio: () => void; + disableAudio: () => void; + enableVideo: () => void; + disableVideo: () => void; + close: () => void; + init: (...args: any[]) => void; + play: (id: string, options?: any) => void; + setVideoEncoderConfiguration: (...args: any[]) => void; + getStats(): any; + getAudioLevel(): number; + playerComp: any; +} + +export type SubscribeOption = { + video?: boolean; + audio?: boolean; +}; + +export type DeviceInstance = { + deviceId: string; + groupId: string; + kind: 'audioinput' | 'audiooutput' | 'videoinput'; + label: string; +}; + +export type StreamOption = { + audio: boolean; + video: boolean; + data?: boolean; + screen?: boolean; + mediaStream?: MediaStream; + microphoneId?: string; + cameraId?: string; +}; diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatBackgroundDrawer.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatBackgroundDrawer.tsx new file mode 100644 index 0000000..ac17233 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatBackgroundDrawer.tsx @@ -0,0 +1,269 @@ +"use client" + +import { useAtom, useSetAtom } from "jotai"; +import { isChatBackgroundDrawerOpenAtom, isCrushLevelDrawerOpenAtom, isCrushLevelRetrieveDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import { InlineDrawer, InlineDrawerContent, InlineDrawerHeader, InlineDrawerDescription, InlineDrawerFooter } from "./InlineDrawer"; +import { Button, IconButton } from "@/components/ui/button"; +import { AiUserImBaseInfoOutput, BackgroundImgListOutput } from "@/services/im/types"; +import { useDelChatBackground, useGetChatBackgroundList, useSetChatBackground } from "@/hooks/useIm"; +import { useChatConfig } from "../../context/chatConfig"; +import { cn } from "@/lib/utils"; +import Image from "next/image"; +import { Checkbox } from "@/components/ui/checkbox"; +import React, { useEffect, useState } from "react"; +import { Tag } from "@/components/ui/tag"; +import { useRouter } from "next/navigation"; +import { useQueryClient } from "@tanstack/react-query"; +import { imKeys } from "@/lib/query-keys"; +import { ImageViewer } from "@/components/ui/image-viewer"; +import { useImageViewer } from "@/hooks/useImageViewer"; +import { AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; + + +const BackgroundImageViewerAction = ({ + aiId, + datas, + backgroundId, + onDeleted, + isSelected, + currentIndex, + onChange, +}: { + aiId: number; + datas: BackgroundImgListOutput[]; + backgroundId: number; + onDeleted?: (nextIndex: number | null) => void; + isSelected: boolean; + currentIndex: number; + onChange: (backgroundId: number) => void; +}) => { + const [isOpen, setIsOpen] = useState(false); + const { mutateAsync: deleteBackground, isPending: isDeletingBackground } = useDelChatBackground({ aiId, backgroundId }) + + const handleDelete = async () => { + await deleteBackground({ aiId, backgroundId }) + const nextLength = datas.length - 1; + if (nextLength <= 0) { + onDeleted?.(null); + return; + } + const isLast = currentIndex >= nextLength; + const nextIndex = isLast ? nextLength - 1 : currentIndex; + onDeleted?.(nextIndex); + setIsOpen(false) + } + + const handleSelect = () => { + // 如果只有一张背景且当前已选中,不允许取消选中 + if (datas.length === 1 && isSelected) { + return; + } + onChange(backgroundId); + } + + return ( + <> +
+
handleSelect()}> + +
Select
+
+ {!!backgroundId &&
} + {!!backgroundId && } + + ) +} + +const BackgroundItem = ({ item, selected, inUse, onClick, onImagePreview, totalCount }: { item: BackgroundImgListOutput, selected: boolean, inUse: boolean, onClick: () => void, onImagePreview: () => void, totalCount: number }) => { + const handleClick = () => { + // 如果只有一张背景且当前已选中,不允许取消选中 + if (totalCount === 1 && selected) { + return; + } + onClick(); + }; + + return ( +
+
+ {''} +
+ {item.isDefault && Default} + {inUse && } + { + e.stopPropagation(); + onImagePreview(); + }} + > + + +
+ ) +} + +const ChatBackgroundDrawer = () => { + const [selectId, setSelectId] = useState() + const { aiId, aiInfo } = useChatConfig(); + const [drawerState, setDrawerState] = useAtom(isChatBackgroundDrawerOpenAtom); + const open = drawerState.open; + const setOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + const router = useRouter(); + const setCrushLevelDrawerState = useSetAtom(isCrushLevelDrawerOpenAtom); + const setIsCrushLevelDrawerOpen = (open: boolean) => setCrushLevelDrawerState(createDrawerOpenState(open)); + + // 图片查看器 + const { + isOpen: isViewerOpen, + currentIndex: viewerIndex, + openViewer, + closeViewer, + handleIndexChange, + } = useImageViewer(); + + const { backgroundImg, aiUserHeartbeatRelation } = aiInfo || {} + const { heartbeatLevelNum } = aiUserHeartbeatRelation || {} + + const isUnlock = heartbeatLevelNum && heartbeatLevelNum >= 10; + + const { data: originBackgroundList } = useGetChatBackgroundList({ aiId }); + const { mutateAsync: updateChatBackground, isPending: isUpdatingChatBackground } = useSetChatBackground({ aiId }) + const queryClient = useQueryClient(); + + + const backgroundList = React.useMemo(() => { + return originBackgroundList?.map(item => ({ + ...item, + backgroundId: item.backgroundId || 0, + })) + }, [originBackgroundList]) + + useEffect(() => { + if (!backgroundList?.length || !aiInfo) return; + const defaultId = backgroundList.find(item => item.imgUrl === backgroundImg)?.backgroundId; + setSelectId(defaultId) + }, [backgroundList, aiInfo]); + + const handleUnlock = () => { + // // todo + // router.push(`/generate/image-2-background?id=${aiId}`); + // return; + + if (!aiId) return; + if (!aiUserHeartbeatRelation) return; + + if (isUnlock) { + router.push(`/generate/image-2-background?id=${aiId}`); + } else { + setIsCrushLevelDrawerOpen(true) + } + } + + const handleConfirm = async () => { + const { imgUrl, isDefault } = backgroundList?.find(item => item.backgroundId === selectId) || {}; + const result = { + aiId, + backgroundId: selectId || '', + } + + if (selectId !== 0) { + result.backgroundId = selectId || ''; + } + await updateChatBackground(result) + + queryClient.setQueryData(imKeys.imUserInfo(aiId), (old: AiUserImBaseInfoOutput) => { + return { + ...old, + backgroundImg: imgUrl, + isDefaultBackground: !!isDefault, + } + }) + setOpen(false) + } + + const handleImagePreview = (index: number) => { + openViewer(backgroundList?.map(item => item.imgUrl || '') || [], index) + } + + return ( + + + Chat Background + +
+
+
+
Generate Image
+
{isUnlock ? 'Unlocked' : 'Unlocks at Lv.10'}
+
+ +
+ +
+ { + backgroundList?.map((item, index) => ( + setSelectId(item.backgroundId)} + onImagePreview={() => handleImagePreview(index)} + totalCount={backgroundList?.length || 0} + /> + )) + } +
+
+
+ + + + +
+ + item.imgUrl || '') || []} + currentIndex={viewerIndex} + open={isViewerOpen} + onClose={closeViewer} + onIndexChange={handleIndexChange} + showChooseButton={false} + ActionComponent={() => { + return ( + { + if (nextIndex === null) { + // 删除后没有图片了 + closeViewer(); + return; + } + // 调整到新的索引,避免越界 + handleIndexChange(nextIndex); + }} + /> + ) + }} + /> +
+ ); +} + +export default ChatBackgroundDrawer; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatButtleDrawer.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatButtleDrawer.tsx new file mode 100644 index 0000000..e296cba --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatButtleDrawer.tsx @@ -0,0 +1,169 @@ +"use client" + +import { isChatButtleDrawerOpenAtom, isCrushLevelDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import { InlineDrawer, InlineDrawerContent, InlineDrawerDescription, InlineDrawerFooter, InlineDrawerHeader } from "./InlineDrawer"; +import { useAtom, useSetAtom } from "jotai"; +import { Button } from "@/components/ui/button"; +import { useGetChatBubbleDictList, useGetMyChatSetting, useSetChatBubble } from "@/hooks/useIm"; +import { useChatConfig } from "../../context/chatConfig"; +import { ChatBubbleListOutput, UnlockType } from "@/services/im/types"; +import Image from "next/image"; +import { Tag } from "@/components/ui/tag"; +import { useEffect, useState } from "react"; +import { useHeartLevelTextFromLevel } from "@/hooks/useHeartLevel"; +import { cn } from "@/lib/utils"; +import ChatBubble from "../ChatMessageItems/ChatBubble"; +import { Checkbox } from "@/components/ui/checkbox"; +import { useRouter } from "next/navigation"; +import { isVipDrawerOpenAtom } from "@/atoms/im"; +import { useCurrentUser } from "@/hooks/auth"; +import { VipType } from "@/services/wallet"; +import { useQueryClient } from "@tanstack/react-query"; +import { imKeys } from "@/lib/query-keys"; + +const ChatButtleItem = ({ item, selected, inUse, onClick }: { item: ChatBubbleListOutput, selected: boolean, inUse: boolean, onClick: () => void }) => { + + return ( +
+
+ + Hi + + {inUse && } + {item.isDefault && Default} + {!item.isUnlock && ( + + + + )} +
+ {item.unlockType === 'MEMBER' ? ( +
+ {item.name} +
+ ) : ( +
+ {item.name} +
+ )} +
+ ) +} + +const ChatButtleDrawer = () => { + const [selectedCode, setSelectedCode] = useState() + const [drawerState, setDrawerState] = useAtom(isChatButtleDrawerOpenAtom); + const open = drawerState.open; + const setOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + const { aiId, aiInfo } = useChatConfig(); + const { chatBubble } = aiInfo || {}; + const setCrushLevelDrawerState = useSetAtom(isCrushLevelDrawerOpenAtom); + const setIsCrushLevelDrawerOpen = (open: boolean) => setCrushLevelDrawerState(createDrawerOpenState(open)); + const setIsVipDrawerOpen = useSetAtom(isVipDrawerOpenAtom); + const queryClient = useQueryClient(); + + const { data: chatBubbleDictList } = useGetChatBubbleDictList({ aiId }) + const { mutateAsync: setChatBubble, isPending: isSettingChatBubble } = useSetChatBubble({ aiId }) + + useEffect(() => { + if (!chatBubbleDictList?.length) return + const defaultCode = chatBubble?.code?.toString(); + setSelectedCode(defaultCode || chatBubbleDictList[0]?.code?.toString()) + }, [chatBubbleDictList, chatBubble]) + + useEffect(() => { + if (open) { + queryClient.invalidateQueries({ queryKey: imKeys.imUserInfo(aiId) }) + const defaultCode = chatBubble?.code?.toString(); + if (defaultCode) { + setSelectedCode(defaultCode); + } + } + }, [open]) + + const handleSetChatBubble = async (code: string) => { + await setChatBubble({ aiId, code }) + setOpen(false) + } + + const renderConfirmButton = () => { + if (!selectedCode) return; + const { isUnlock, unlockType, unlockHeartbeatLevel } = chatBubbleDictList?.find(item => item.code === selectedCode) || {}; + if (isUnlock || isUnlock === null) { + return ( + + ) + } + + if (unlockType === UnlockType.Member) { + return ( + + ) + } + + if (unlockType === UnlockType.HeartbeatLevel) { + return ( + + ) + } + + return null; + } + + return ( + + + Chat Buttles + +
+ { + chatBubbleDictList?.map(item => ( + { + setSelectedCode(item.code) + }} + /> + )) + } +
+
+ + + {renderConfirmButton()} + +
+
+ ); +}; + +export default ChatButtleDrawer; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatModelDrawer.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatModelDrawer.tsx new file mode 100644 index 0000000..838aa2b --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatModelDrawer.tsx @@ -0,0 +1,98 @@ +"use client" + +import { isChatModelDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import { InlineDrawer, InlineDrawerContent, InlineDrawerDescription, InlineDrawerFooter, InlineDrawerHeader } from "./InlineDrawer"; +import { useAtom } from "jotai"; +import { Button, IconButton } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import Image from "next/image"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { useGetChatModelDictList } from "@/hooks/useIm"; +import { useEffect } from "react"; +import { ChatPriceType, ChatPriceTypeMap } from "@/hooks/useWallet"; + + + +const ChatModelDrawer = () => { + const [drawerState, setDrawerState] = useAtom(isChatModelDrawerOpenAtom); + const open = drawerState.open; + const setOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + + const { data: chatModelDictList } = useGetChatModelDictList(); + + console.log('chatModelDictList', chatModelDictList) + + return ( + + + Chat Model + +
+
+
+
Role-Playing Model
+ + + + + +
+

Text Message Price: Refers to the cost of chatting with the character via text messages, including sending text, images, or gifts. Charged per message.

+

Voice Message Price: Refers to the cost of sending a voice message to the character or playing the character’s voice. Charged per use.

+

Voice Call Price: Refers to the cost of having a voice call with the character. Charged per minute.

+
+
+
+
+ +
+
Role-play a conversation with AI
+ +
+
+ diamond + + {ChatPriceTypeMap[ChatPriceType.TEXT]}/Text Message + +
+ +
+
+ diamond + + {ChatPriceTypeMap[ChatPriceType.VOICE]}/Send or play voice + +
+
+ +
+
+ diamond + + {ChatPriceTypeMap[ChatPriceType.VOICE_CALL]}/min Voice call + +
+
+
+
+
More models coming soon
+
+ + + + +
+
+ ); +} + +export default ChatModelDrawer; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileAction.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileAction.tsx new file mode 100644 index 0000000..4ee285d --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileAction.tsx @@ -0,0 +1,57 @@ +"use client" + +import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { IconButton } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; +import DeleteMessageDialog from "./DeleteMessageDialog"; +import { useState } from "react"; +import useShare from "@/hooks/useShare"; +import { useChatConfig } from "../../../context/chatConfig"; + +const ChatProfileAction = () => { + const [deleteMessageDialogOpen, setDeleteMessageDialogOpen] = useState(false); + const { shareFacebook, shareTwitter } = useShare(); + const { aiId } = useChatConfig(); + + const handleDeleteMessage = () => { + setDeleteMessageDialogOpen(true); + } + + const handleShareFacebook = () => { + shareFacebook({ text: 'Come to Crushlevel for chat, Crush, and AI - chat.', shareUrl: `${process.env.NEXT_PUBLIC_APP_URL}/@${aiId}` }); + } + const handleShareTwitter = () => { + shareTwitter({ text: 'Come to Crushlevel for chat, Crush, and AI - chat.', shareUrl: `${process.env.NEXT_PUBLIC_APP_URL}/@${aiId}` }); + } + + return ( +
+ + + + + + + + Share to Facebook + + + + Share to X + +
+ +
+ + + Delete + +
+
+ + +
+ ); +} + +export default ChatProfileAction; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileLikeAction.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileLikeAction.tsx new file mode 100644 index 0000000..8b6dede --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileLikeAction.tsx @@ -0,0 +1,14 @@ +"use client" +import ChatProfileShareIcon from "./ChatProfileShareIcon"; +import ChatProfileLikeIcon from "./ChatProfileLikeIcon"; + +const ChatProfileLikeAction = () => { + return ( +
+ + +
+ ); +} + +export default ChatProfileLikeAction; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileLikeIcon.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileLikeIcon.tsx new file mode 100644 index 0000000..48d294e --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileLikeIcon.tsx @@ -0,0 +1,50 @@ +"use client" +import { IconButton } from "@/components/ui/button"; +import { useGetAIUserBaseInfo } from "@/hooks/aiUser"; +import { useDoAiUserLiked } from "@/hooks/useCommon"; +import { aiUserKeys, imKeys } from "@/lib/query-keys"; +import { useQueryClient } from "@tanstack/react-query"; +import { useParams } from "next/navigation"; +import { useChatConfig } from "../../../context/chatConfig"; + +const ChatProfileLikeIcon = () => { + const { aiId, aiInfo } = useChatConfig(); + + const { mutateAsync: doAiUserLiked } = useDoAiUserLiked(); + const { liked } = aiInfo || {}; + const queryClient = useQueryClient(); + + const handleLike = () => { + doAiUserLiked({ aiId: Number(aiId), likedStatus: liked ? 'CANCELED' : 'LIKED' }); + queryClient.setQueryData(aiUserKeys.baseInfo({ aiId: Number(aiId) }), (oldData: any) => { + return { + ...oldData, + liked: !liked, + } + }); + queryClient.setQueryData(imKeys.imUserInfo(Number(aiId)), (oldData: any) => { + return { + ...oldData, + liked: !liked, + } + }); + queryClient.setQueryData(aiUserKeys.stat({ aiId: Number(aiId) }), (oldData: any) => { + return { + ...oldData, + likedNum: !liked ? oldData.likedNum + 1 : oldData.likedNum - 1, + } + }); + } + + return ( + + {!liked ? : } + + ); +} + +export default ChatProfileLikeIcon; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfilePersona.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfilePersona.tsx new file mode 100644 index 0000000..20092ac --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfilePersona.tsx @@ -0,0 +1,54 @@ +"use client" + +import { isChatProfileEditDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import { useGetMyChatSetting } from "@/hooks/useIm"; +import { useSetAtom } from "jotai"; +import { useChatConfig } from "../../../context/chatConfig"; +import { Gender } from "@/types/user"; +import { cn, getAge } from "@/lib/utils"; + + +const ChatProfilePersona = () => { + const { aiId } = useChatConfig(); + const setDrawerState = useSetAtom(isChatProfileEditDrawerOpenAtom); + const setIsChatProfileEditDrawerOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + const { data: chatSettingData } = useGetMyChatSetting({ aiId }); + + const { nickname, sex, birthday, whoAmI } = chatSettingData || {}; + + const genderMap = { + [Gender.MALE]: 'Male', + [Gender.FEMALE]: 'Female', + [Gender.OTHER]: 'Unspecified', + } + + return ( +
+
+
My Chat Persona
+
setIsChatProfileEditDrawerOpen(true)}>Edit
+
+ +
+
+
Nickname
+
{nickname}
+
+
+
Gender
+
{genderMap[sex as keyof typeof genderMap]}
+
+
+
Age
+
{getAge(Number(birthday))}
+
+
+
Who am I
+
{whoAmI || 'Unfilled'}
+
+
+
+ ); +} + +export default ChatProfilePersona; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileShareIcon.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileShareIcon.tsx new file mode 100644 index 0000000..b4f6f70 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/ChatProfileShareIcon.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { IconButton } from "@/components/ui/button"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import useShare from "@/hooks/useShare"; +import { useParams } from "next/navigation"; + +const ChatProfileShareIcon = () => { + const { userId } = useParams(); + + const { shareFacebook, shareTwitter } = useShare(); + + const handleShareFacebook = () => { + shareFacebook({ text: 'Come to Crushlevel for chat, Crush, and AI - chat.', shareUrl: `${process.env.NEXT_PUBLIC_APP_URL}/@${userId}` }); + } + const handleShareTwitter = () => { + shareTwitter({ text: 'Come to Crushlevel for chat, Crush, and AI - chat.', shareUrl: `${process.env.NEXT_PUBLIC_APP_URL}/@${userId}` }); + } + + return ( + + + + + + + + + + Share to Facebook + + + + Share to X + + + + ); +} + +export default ChatProfileShareIcon; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/DeleteMessageDialog.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/DeleteMessageDialog.tsx new file mode 100644 index 0000000..b27e9a2 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/DeleteMessageDialog.tsx @@ -0,0 +1,61 @@ +"use client" +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; +import { useNimConversation, useNimMsgContext } from "@/context/NimChat/useNimChat"; +import { useAtomValue, useSetAtom } from "jotai"; +import { selectedConversationIdAtom } from "@/atoms/im"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { useQueryClient } from "@tanstack/react-query"; +import { imKeys } from "@/lib/query-keys"; +import { useChatConfig } from "../../../context/chatConfig"; +import { useDeleteConversations } from "@/hooks/useIm"; + +const DeleteMessageDialog = ({ + open, + onOpenChange, +}: { + open: boolean; + onOpenChange: (open: boolean) => void; +}) => { + const { aiId } = useChatConfig(); + const { removeConversationById } = useNimConversation(); + const { clearHistoryMessage } = useNimMsgContext(); + const selectedConversationId = useAtomValue(selectedConversationIdAtom); + const [loading, setLoading] = useState(false); + const setSelectedConversationId = useSetAtom(selectedConversationIdAtom); + const router = useRouter(); + const queryClient = useQueryClient(); + + const { mutateAsync: deleteConversations } = useDeleteConversations(); + + + const handleDeleteMessage = async () => { + if (!selectedConversationId) return; + setLoading(true); + await removeConversationById(selectedConversationId); + await clearHistoryMessage(selectedConversationId); + await deleteConversations({ aiIdList: [aiId] }); + setSelectedConversationId(null); + router.push("/chat") + queryClient.invalidateQueries({ queryKey: imKeys.imUserInfo(aiId) }) + setLoading(false); + onOpenChange(false); + } + + return ( + + + + Delete + + Deletion is permanent. Your accumulated Affection points and the character's memories will not be affected. Please confirm deletion. + + Cancel + Delete + + + + ); +} + +export default DeleteMessageDialog; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/index.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/index.tsx new file mode 100644 index 0000000..50eeb02 --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileDrawer/index.tsx @@ -0,0 +1,255 @@ +import { InlineDrawer, InlineDrawerContent } from "../InlineDrawer"; +import { useAtom, useSetAtom } from "jotai"; +import { isChatBackgroundDrawerOpenAtom, isChatProfileDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; +import { Tag } from "@/components/ui/tag"; +import { Switch } from "@/components/ui/switch"; +import { useEffect, useState } from "react"; +import ChatProfileAction from "./ChatProfileAction"; +import { useChatConfig } from "../../../context/chatConfig"; +import Image from "next/image"; +import { formatNumberToKMB, getAge } from "@/lib/utils"; +import { useGetAIUserStat } from "@/hooks/aiUser"; +import { IconButton } from "@/components/ui/button"; +import { useGetMyChatSetting, useSetAutoPlayVoice } from "@/hooks/useIm"; +import ChatProfilePersona from "./ChatProfilePersona"; +import { isChatButtleDrawerOpenAtom, isChatModelDrawerOpenAtom } from "@/atoms/chat"; +import { Badge } from "@/components/ui/badge"; +import { useRedDot, RED_DOT_KEYS } from "@/hooks/useRedDot"; +import { imKeys } from "@/lib/query-keys"; +import { AiUserImBaseInfoOutput } from "@/services/im"; +import { useQueryClient } from "@tanstack/react-query"; +import CrushLevelAvatar from "../../CrushLevelAvatar"; +import ChatProfileLikeAction from "./ChatProfileLikeAction"; +import { isVipDrawerOpenAtom } from "@/atoms/im"; +import { useCurrentUser } from "@/hooks/auth"; +import { VipType } from "@/services/wallet"; +import Decimal from "decimal.js"; + +const genderMap = { + 0: '/icons/male.svg', + 1: '/icons/female.svg', + 2: '/icons/gender-neutral.svg' +} + +const ChatProfileDrawer = () => { + const [drawerState, setDrawerState] = useAtom(isChatProfileDrawerOpenAtom); + const isChatProfileDrawerOpen = drawerState.open; + const setIsChatProfileDrawerOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + + const setModelDrawerState = useSetAtom(isChatModelDrawerOpenAtom); + const setIsChatModelDrawerOpen = (open: boolean) => setModelDrawerState(createDrawerOpenState(open)); + + const setButtleDrawerState = useSetAtom(isChatButtleDrawerOpenAtom); + const setIsChatButtleDrawerOpen = (open: boolean) => setButtleDrawerState(createDrawerOpenState(open)); + + const setBackgroundDrawerState = useSetAtom(isChatBackgroundDrawerOpenAtom); + const setIsChatBackgroundDrawerOpen = (open: boolean) => setBackgroundDrawerState(createDrawerOpenState(open)); + const setIsVipDrawerOpen = useSetAtom(isVipDrawerOpenAtom); + + const { aiId, aiInfo } = useChatConfig(); + const { data: user } = useCurrentUser() || {}; + const { isMember } = user || {}; + + const isOwner = user?.userId === aiInfo?.userId; + + // 使用红点管理hooks + const { hasRedDot, markAsViewed } = useRedDot(); + + const { data: statData } = useGetAIUserStat({ aiId }); + const { sex, birthday, characterName, tagName, chatBubble, isDefaultBackground, isAutoPlayVoice, aiUserHeartbeatRelation } = aiInfo || {}; + const { likedNum, chatNum, conversationNum, coinNum } = statData || {}; + const { heartbeatLevel } = aiUserHeartbeatRelation || {}; + + const { mutate: setAutoPlayVoice } = useSetAutoPlayVoice(); + const queryClient = useQueryClient(); + + useEffect(() => { + if (isChatProfileDrawerOpen) { + queryClient.invalidateQueries({ queryKey: imKeys.imUserInfo(aiId) }) + } + }, [isChatProfileDrawerOpen]) + + const statList = [ + { + label: 'Liked', + value: formatNumberToKMB(likedNum || 0), + }, + { + label: 'Chat', + value: formatNumberToKMB(chatNum || 0), + }, + { + label: 'People', + value: formatNumberToKMB(conversationNum || 0), + }, + isOwner && { + label: 'Crush Coin', + value: formatNumberToKMB(new Decimal(coinNum || 0).div(100).toNumber()), + }, + ].filter(Boolean) as { label: string; value: string | number }[]; + + const handleClose = () => { + setIsChatProfileDrawerOpen(false); + }; + + return ( + + + {/*
*/} +
+ {/* Header with back and more buttons */} + + + +
+ +
+ + + {/* Name and Tags */} +
+
+ + Gender +
{getAge(Number(birthday))}
+
+ {characterName} + {tagName} +
+
+ + + + {/* Statistics */} +
+ { + statList.map((item, index) => ( +
+
{item.value}
+
{item.label}
+
+ )) + } +
+ + + + {/* Chat Setting */} +
+
+ Chat Setting +
+
+
{ + setIsChatModelDrawerOpen(true) + }} + > +
Chat Model
+
+
Role-Playing
+ +
+
+ +
+
{ + markAsViewed(RED_DOT_KEYS.CHAT_BUBBLE); + setIsChatButtleDrawerOpen(true); + }} + > +
+
+
Chat Bubble
+ {hasRedDot(RED_DOT_KEYS.CHAT_BUBBLE) && } +
+
+
+ {/*
{chatBubble?.name || 'Default'}
*/} + +
+
+ +
{ + markAsViewed(RED_DOT_KEYS.CHAT_BACKGROUND); + setIsChatBackgroundDrawerOpen(true); + }} + > +
+
+
Chat Background
+ {hasRedDot(RED_DOT_KEYS.CHAT_BACKGROUND) && } +
+
+
+ {/*
{isDefaultBackground ? 'Default' : ''}
*/} + +
+
+
+ +
{ + const checked = !isAutoPlayVoice; + if (!isMember) { + setIsVipDrawerOpen({ open: true, vipType: VipType.AUTO_PLAY_VOICE }) + return; + } + queryClient.setQueryData(imKeys.imUserInfo(aiId), (oldData: AiUserImBaseInfoOutput) => { + return { + ...oldData, + isAutoPlayVoice: checked, + } + }) + setAutoPlayVoice({ aiId, isAutoPlayVoice: checked }) + }} + > +
Auto play voice
+
+ +
+
+
+
+
+
+
+ + + ); +} + +export default ChatProfileDrawer; \ No newline at end of file diff --git a/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx new file mode 100644 index 0000000..be8a9fd --- /dev/null +++ b/src/app/(main)/chat/[aiId]/components/ChatDrawers/ChatProfileEditDrawer.tsx @@ -0,0 +1,379 @@ +import { useEffect, useState, useCallback } from "react"; +import { InlineDrawer, InlineDrawerContent, InlineDrawerDescription, InlineDrawerFooter, InlineDrawerHeader } from "./InlineDrawer"; +import { z } from "zod"; +import dayjs from "dayjs"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Gender } from "@/types/user"; +import { Input } from "@/components/ui/input"; +import GenderInput from "@/components/features/genderInput"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Label } from "@/components/ui/label"; +import { isChatProfileEditDrawerOpenAtom, createDrawerOpenState } from "@/atoms/chat"; +import { useAtom } from "jotai"; +import { calculateAge, getDaysInMonth } from "@/lib/utils"; +import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/ui/button"; +import { useGetMyChatSetting, useSetMyChatSetting } from "@/hooks/useIm"; +import { useChatConfig } from "../../context/chatConfig"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle +} from "@/components/ui/alert-dialog"; +import { useCheckNickname } from "@/hooks/auth"; + +const currentYear = dayjs().year() +const years = Array.from({ length: currentYear - 1950 + 1 }, (_, i) => `${1950 + i}`) +const months = Array.from({ length: 12 }, (_, i) => `${i + 1}`.padStart(2, "0")) +const monthTexts = Array.from({ length: 12 }, (_, i) => dayjs().month(i).format('MMM')) + +const characterFormSchema = z.object({ + nickname: z.string().trim().min(1, "Please Enter nickname").min(2, "Nickname must be between 2 and 20 characters").max(20, "Nickname must be less than 20 characters"), + sex: z.enum(Gender, { message: "Please select gender" }), + year: z.string().min(1, "Please select year"), + month: z.string().min(1, "Please select month"), + day: z.string().min(1, "Please select day"), + profile: z.string().trim().optional(), +}).refine((data) => { + const age = calculateAge(data.year, data.month, data.day) + return age >= 18 +}, { + message: "Character age must be at least 18 years old", + path: ["year"] +}).refine((data) => { + if (data.profile) { + if (data.profile.trim().length > 300) { + return false; + } + return data.profile.trim().length >= 10; + } + return true; +}, { + message: "At least 10 characters", + path: ["profile"] +}) + +const ChatProfileEditDrawer = () => { + const [loading, setLoading] = useState(false) + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + const { aiId } = useChatConfig(); + const [drawerState, setDrawerState] = useAtom(isChatProfileEditDrawerOpenAtom); + const open = drawerState.open; + const setOpen = (open: boolean) => setDrawerState(createDrawerOpenState(open)); + const { data: chatSettingData } = useGetMyChatSetting({ aiId }); + const { mutateAsync: setMyChatSetting } = useSetMyChatSetting({ aiId }); + + const birthday = chatSettingData?.birthday ? dayjs(chatSettingData.birthday) : undefined; + + const form = useForm>({ + resolver: zodResolver(characterFormSchema), + defaultValues: { + nickname: chatSettingData?.nickname || "", + sex: chatSettingData?.sex || undefined, + year: birthday?.year().toString() || undefined, + month: birthday?.month() !== undefined ? (birthday.month() + 1).toString().padStart(2, '0') : undefined, + day: birthday?.date().toString().padStart(2, '0') || undefined, + profile: chatSettingData?.whoAmI || "", + }, + }) + + const { mutateAsync: checkNickname } = useCheckNickname({ + onError: (error) => { + form.setError("nickname", { + message: error.errorMsg, + }) + } + }); + + // 处理关闭抽屉的逻辑 + const handleCloseDrawer = useCallback(() => { + if (form.formState.isDirty) { + setShowConfirmDialog(true); + } else { + setOpen(false); + } + }, [form.formState.isDirty, setOpen]); + + // 确认放弃修改 + const handleConfirmDiscard = useCallback(() => { + form.reset(); + setShowConfirmDialog(false); + setOpen(false); + }, [form, setOpen]); + + // 取消放弃修改 + const handleCancelDiscard = useCallback(() => { + setShowConfirmDialog(false); + }, []); + + useEffect(() => { + if (chatSettingData) { + form.reset({ + nickname: chatSettingData.nickname, + sex: chatSettingData.sex, + year: birthday?.year().toString(), + month: birthday?.month() !== undefined ? (birthday.month() + 1).toString().padStart(2, '0') : undefined, + day: birthday?.date().toString().padStart(2, '0'), + profile: chatSettingData.whoAmI || '', + }) + } + }, [chatSettingData]) + + // ESC键关闭抽屉 + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape' && open) { + handleCloseDrawer(); + } + }; + + if (open) { + document.addEventListener('keydown', handleKeyDown); + } + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [open, handleCloseDrawer]); + + async function onSubmit(data: z.infer) { + if (!form.formState.isDirty) { + setOpen(false) + return; + } + setLoading(true) + try { + const isExist = await checkNickname({ + nickname: data.nickname.trim(), + }) + if (isExist) { + form.setError("nickname", { + message: "This nickname is already taken", + }) + return; + } + await setMyChatSetting({ + aiId, + nickname: data.nickname, + birthday: new Date(`${data.year}-${data.month}-${data.day}`).getTime(), + whoAmI: data.profile || '', + }) + setOpen(false) + } catch (error) { + console.error(error) + } finally { + setLoading(false) + } + } + + const selectedYear = form.watch("year") + const selectedMonth = form.watch("month") + const days = selectedYear && selectedMonth ? getDaysInMonth(selectedYear, selectedMonth) : [] + + const genderTexts = [ + { + value: Gender.MALE, + label: 'Male', + }, + { + value: Gender.FEMALE, + label: 'Female', + }, + { + value: Gender.OTHER, + label: 'Other', + }, + ] + + const gender = form.watch("sex"); + const genderText = genderTexts.find(text => text.value === gender)?.label; + + return ( + <> + { + if (!isOpen) { + handleCloseDrawer(); + } else { + setOpen(isOpen); + } + }} + timestamp={drawerState.timestamp} + > + + My Chat Persona + +
+ + ( + + Nickname + + + + + + )} + /> +
+
Gender
+ +
+
+ {genderText} +
+
+ Please note: gender cannot be changed after setting +
+
+
+ {/* ( + + Gender + + + + + )} + /> */} +
+ +
+ ( + + + + + + )} + /> + ( + + + + + + )} + /> + ( + + + + + + )} + /> +
+ + {form.formState.errors.year?.message || form.formState.errors.month?.message || form.formState.errors.day?.message} + +
+ + ( + + My Persona(Optional) + +