feat: 初始化仓库
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
<div className="bg-primary-gradient-normal hover:bg-primary-gradient-hover">
|
||||
// 内容
|
||||
</div>
|
||||
```
|
||||
|
||||
2. 字体使用:
|
||||
```tsx
|
||||
<h1 className="txt-display-l">大标题</h1>
|
||||
<p className="txt-body-l">正文内容</p>
|
||||
<span className="txt-numDisplay-xl">12345</span>
|
||||
```
|
||||
|
||||
3. 响应式设计:
|
||||
所有工具类都支持响应式前缀:
|
||||
- sm: ≥768px
|
||||
- md: ≥1024px
|
||||
- lg: ≥1280px
|
||||
- xl: ≥1536px
|
||||
|
||||
例如:
|
||||
```tsx
|
||||
<h1 className="txt-display-s md:txt-display-m lg:txt-display-l">
|
||||
响应式标题
|
||||
</h1>
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -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 <path>`—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.
|
||||
|
|
@ -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` 等
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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`),替换了之前的模拟数据。
|
||||
|
||||
## 后续扩展
|
||||
|
||||
- 添加建议个性化定制
|
||||
- 支持更多建议类型
|
||||
- 添加使用统计和优化
|
||||
- 优化缓存策略和错误处理
|
||||
|
|
@ -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<string>("")
|
||||
|
||||
const handleAvatarChange = (avatarUrl: string) => {
|
||||
setCurrentAvatar(avatarUrl)
|
||||
// 这里可以调用API保存头像
|
||||
}
|
||||
|
||||
const handleAvatarDelete = () => {
|
||||
setCurrentAvatar("")
|
||||
// 这里可以调用API删除头像
|
||||
}
|
||||
|
||||
const openAvatarSetting = () => {
|
||||
setIsAvatarSettingOpen(true)
|
||||
}
|
||||
|
||||
const closeAvatarSetting = () => {
|
||||
setIsAvatarSettingOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* 触发按钮 */}
|
||||
<button onClick={openAvatarSetting}>
|
||||
设置头像
|
||||
</button>
|
||||
|
||||
{/* 头像设置模态框 */}
|
||||
<AvatarSetting
|
||||
isOpen={isAvatarSettingOpen}
|
||||
onClose={closeAvatarSetting}
|
||||
currentAvatar={currentAvatar}
|
||||
onAvatarChange={handleAvatarChange}
|
||||
onAvatarDelete={handleAvatarDelete}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 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. 裁剪弹窗的层级比头像设置模态框更高
|
||||
|
|
@ -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';
|
||||
|
||||
// 基础使用
|
||||
<CrushLevelAvatar />
|
||||
|
||||
// 大尺寸带动画
|
||||
<CrushLevelAvatar
|
||||
size="large"
|
||||
showAnimation={true}
|
||||
/>
|
||||
```
|
||||
|
||||
## 依赖要求
|
||||
|
||||
组件需要在以下上下文中使用:
|
||||
|
||||
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图标 */}
|
||||
<div className={cn(
|
||||
"absolute left-1/2 top-[-63px] -translate-x-1/2 w-60 h-[210px] pointer-events-none",
|
||||
isLevelChanging && showAnimation && "animate-scale-fade-out"
|
||||
)}>
|
||||
<Image
|
||||
src="/icons/crushlevel_heart.svg"
|
||||
alt="heart background"
|
||||
fill
|
||||
className="object-contain opacity-20"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 心动等级徽章
|
||||
```tsx
|
||||
{/* 心形背景 + 等级数字 */}
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 size-10 z-10">
|
||||
<Image
|
||||
src="/icons/crushlevel_heart.svg"
|
||||
alt="heart"
|
||||
width={40}
|
||||
height={40}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"relative z-10 font-bold text-white text-base transition-all duration-300",
|
||||
isLevelChanging && "animate-level-change"
|
||||
)}
|
||||
key={displayLevel}
|
||||
>
|
||||
{displayLevel?.replace('LEVEL_', '') || '1'}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 动画触发机制
|
||||
```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:优化性能和用户体验
|
||||
|
|
@ -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 | |
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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<void> => {
|
||||
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 自动处理安全验证
|
||||
|
||||
强烈建议新项目直接使用这种方式!
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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 实现参考
|
||||
|
||||
|
|
@ -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 (
|
||||
<div>
|
||||
<button
|
||||
onClick={handleLike}
|
||||
className={currentStatus === MessageLikeStatus.Liked ? 'active' : ''}
|
||||
>
|
||||
👍
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDislike}
|
||||
className={currentStatus === MessageLikeStatus.Disliked ? 'active' : ''}
|
||||
>
|
||||
👎
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 高级用法
|
||||
|
||||
```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实现
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
# URL Text 参数功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
实现了从 URL 参数中获取 `text` 并自动填充到聊天输入框的功能。用户点击对话建议时,会跳转到聊天页面并自动填充对应的文本。
|
||||
|
||||
## 实现细节
|
||||
|
||||
### 1. StartChatItem 组件
|
||||
|
||||
**文件位置**: `src/app/(main)/home/components/StartChat/StartChatItem.tsx`
|
||||
|
||||
**功能**:
|
||||
- 对话建议列表中的每一项都是一个链接
|
||||
- 点击时跳转到聊天页面,并将建议文本作为 URL 参数传递
|
||||
- 使用 `encodeURIComponent()` 对文本进行编码,确保特殊字符正确传递
|
||||
|
||||
**示例**:
|
||||
```tsx
|
||||
<Link
|
||||
href={`/chat/${character.aiId}?text=${encodeURIComponent(suggestion)}`}
|
||||
className="..."
|
||||
>
|
||||
<span>{suggestion}</span>
|
||||
</Link>
|
||||
```
|
||||
|
||||
### 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` - 音频播放上下文(相关功能)
|
||||
|
||||
|
|
@ -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 (
|
||||
<button
|
||||
onClick={() => generateAndPlay(config)}
|
||||
disabled={isGenerating}
|
||||
>
|
||||
{isGenerating ? '生成中...' : isPlaying(config) ? '播放中' : '播放'}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 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 (
|
||||
<VoiceSelector
|
||||
value={voiceConfig}
|
||||
onChange={setVoiceConfig}
|
||||
onPlay={handlePlayVoice} // 预设语音播放回调
|
||||
playing={isPlayingPreset} // 预设语音播放状态
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 接口说明
|
||||
|
||||
### 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. **状态管理**:精确的状态控制,避免不必要的重渲染
|
||||
|
||||
## 扩展功能
|
||||
|
||||
- 支持自定义缓存策略
|
||||
- 支持批量预生成常用语音
|
||||
- 支持语音质量选择
|
||||
- 支持播放进度控制
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
@ -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']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 443 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9728 1.5C15.1725 1.5 13.3749 2.19731 12.0013 3.59462C10.6278 2.19731 8.83014 1.5 7.02984 1.5C5.22954 1.5 3.43191 2.19731 2.05834 3.59462C-0.686114 6.38654 -0.686114 10.9123 2.05834 13.7069L9.7903 21.5712C10.4011 22.1904 11.2012 22.5 12.0013 22.5C12.8015 22.5 13.6016 22.1904 14.2097 21.5685L21.9417 13.7042C24.6861 10.9123 24.6861 6.38654 21.9417 3.59192C20.5708 2.19731 18.7731 1.5 16.9728 1.5Z" fill="url(#paint0_radial_161_7136)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_161_7136" cx="0" cy="0" r="1" gradientTransform="matrix(24 24.7812 -25.4804 23.3013 4.71622 1.50006)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 884 B |
|
|
@ -0,0 +1,14 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_1035)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 3H25C27.2091 3 29 4.79086 29 7V25C29 27.2091 27.2091 29 25 29H9C6.79086 29 5 27.2091 5 25V7C5 4.79086 6.79086 3 9 3ZM12 8C12 9.10457 11.1046 10 10 10C8.89543 10 8 9.10457 8 8C8 6.89543 8.89543 6 10 6C11.1046 6 12 6.89543 12 8ZM10 18C11.1046 18 12 17.1046 12 16C12 14.8954 11.1046 14 10 14C8.89543 14 8 14.8954 8 16C8 17.1046 8.89543 18 10 18ZM10 26C11.1046 26 12 25.1046 12 24C12 22.8954 11.1046 22 10 22C8.89543 22 8 22.8954 8 24C8 25.1046 8.89543 26 10 26Z" fill="#484151"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00244 7.00073H10.0024C10.5547 7.00073 11.0024 7.44845 11.0024 8.00073C11.0024 8.55302 10.5547 9.00073 10.0024 9.00073H4.00244C3.45016 9.00073 3.00244 8.55302 3.00244 8.00073C3.00244 7.44845 3.45016 7.00073 4.00244 7.00073Z" fill="#352E3E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00244 15.0007H10.0024C10.5547 15.0007 11.0024 15.4484 11.0024 16.0007C11.0024 16.553 10.5547 17.0007 10.0024 17.0007H4.00244C3.45016 17.0007 3.00244 16.553 3.00244 16.0007C3.00244 15.4484 3.45016 15.0007 4.00244 15.0007Z" fill="#352E3E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00244 23.0007H10.0024C10.5547 23.0007 11.0024 23.4484 11.0024 24.0007C11.0024 24.553 10.5547 25.0007 10.0024 25.0007H4.00244C3.45016 25.0007 3.00244 24.553 3.00244 24.0007C3.00244 23.4484 3.45016 23.0007 4.00244 23.0007Z" fill="#352E3E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 14C24 16.2091 22.2091 18 20 18C17.7909 18 16 16.2091 16 14C16 11.7909 17.7909 10 20 10C22.2091 10 24 11.7909 24 14ZM20 19C20.8539 19 21.6506 19.4295 22.12 20.1428L23.2231 21.8193C23.5564 22.3259 23.1931 23 22.5866 23H17.4134C16.8069 23 16.4436 22.3259 16.7769 21.8193L17.88 20.1428C18.3494 19.4295 19.1461 19 20 19Z" fill="#847D8B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_164_1035">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,34 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_1076)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 3H25C27.2091 3 29 4.79086 29 7V25C29 27.2091 27.2091 29 25 29H9C6.79086 29 5 27.2091 5 25V7C5 4.79086 6.79086 3 9 3ZM12 8C12 9.10457 11.1046 10 10 10C8.89543 10 8 9.10457 8 8C8 6.89543 8.89543 6 10 6C11.1046 6 12 6.89543 12 8ZM10 18C11.1046 18 12 17.1046 12 16C12 14.8954 11.1046 14 10 14C8.89543 14 8 14.8954 8 16C8 17.1046 8.89543 18 10 18ZM10 26C11.1046 26 12 25.1046 12 24C12 22.8954 11.1046 22 10 22C8.89543 22 8 22.8954 8 24C8 25.1046 8.89543 26 10 26Z" fill="url(#paint0_linear_164_1076)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00244 7.00073H10.0024C10.5547 7.00073 11.0024 7.44845 11.0024 8.00073C11.0024 8.55302 10.5547 9.00073 10.0024 9.00073H4.00244C3.45016 9.00073 3.00244 8.55302 3.00244 8.00073C3.00244 7.44845 3.45016 7.00073 4.00244 7.00073Z" fill="url(#paint1_radial_164_1076)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00244 15.0007H10.0024C10.5547 15.0007 11.0024 15.4484 11.0024 16.0007C11.0024 16.553 10.5547 17.0007 10.0024 17.0007H4.00244C3.45016 17.0007 3.00244 16.553 3.00244 16.0007C3.00244 15.4484 3.45016 15.0007 4.00244 15.0007Z" fill="url(#paint2_radial_164_1076)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00244 23.0007H10.0024C10.5547 23.0007 11.0024 23.4484 11.0024 24.0007C11.0024 24.553 10.5547 25.0007 10.0024 25.0007H4.00244C3.45016 25.0007 3.00244 24.553 3.00244 24.0007C3.00244 23.4484 3.45016 23.0007 4.00244 23.0007Z" fill="url(#paint3_radial_164_1076)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 14C24 16.2091 22.2091 18 20 18C17.7909 18 16 16.2091 16 14C16 11.7909 17.7909 10 20 10C22.2091 10 24 11.7909 24 14ZM20 19C20.8539 19 21.6506 19.4295 22.12 20.1428L23.2231 21.8193C23.5564 22.3259 23.1931 23 22.5866 23H17.4134C16.8069 23 16.4436 22.3259 16.7769 21.8193L17.88 20.1428C18.3494 19.4295 19.1461 19 20 19Z" fill="url(#paint4_radial_164_1076)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_164_1076" x1="5" y1="16" x2="29" y2="16" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint1_radial_164_1076" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4.57452 7.00073) rotate(16.4368) scale(8.34087 8.5025)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint2_radial_164_1076" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4.57452 15.0007) rotate(16.4368) scale(8.34087 8.5025)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint3_radial_164_1076" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4.57452 23.0007) rotate(16.4368) scale(8.34087 8.5025)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint4_radial_164_1076" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.5721 10) rotate(62.4586) scale(17.3014 17.0843)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_164_1076">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
|
@ -0,0 +1,33 @@
|
|||
<svg width="148" height="148" viewBox="0 0 148 148" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_33_1726)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M50.0053 86.0318C48.3943 82.5221 47.5889 78.6445 47.5889 74.399C47.5889 70.0392 48.4343 66.0789 50.1251 62.518C50.402 61.9349 50.7015 61.3624 51.0238 60.8007C52.2557 58.6646 53.7275 56.778 55.4392 55.1412C56.926 53.7193 58.5939 52.4858 60.4427 51.4406C61.2731 50.9737 62.1243 50.5555 62.9961 50.1861C66.3003 48.7861 69.9025 48.0861 73.8028 48.0861C78.5201 48.0861 82.7408 49.0454 86.465 50.9639C87.3461 51.4178 88.1994 51.9254 89.0249 52.4866C90.5389 53.516 91.8942 54.6891 93.0906 56.0057C95.3039 58.4412 96.9738 61.3681 98.1003 64.7864L86.1685 64.7864C85.2567 62.8837 84.0576 61.3393 82.5713 60.1531C82.1322 59.8027 81.668 59.4835 81.1788 59.1956C81.0484 59.1196 80.9168 59.0459 80.7839 58.9745C78.7304 57.8715 76.3793 57.32 73.7305 57.32C71.2517 57.32 68.9973 57.796 66.9671 58.748C66.5295 58.9532 66.1023 59.1806 65.6855 59.4301C64.9875 59.85 64.3351 60.3225 63.7284 60.8474C62.3028 62.0809 61.1292 63.6043 60.2077 65.4176C58.9061 68.0026 58.2552 70.9964 58.2552 74.399C58.2552 77.8377 58.9061 80.8375 60.2077 83.3984C61.1013 85.1403 62.2318 86.6153 63.5994 87.8232C64.2424 88.3911 64.9377 88.9 65.6855 89.3499C66.1023 89.5994 66.5295 89.8267 66.9671 90.032C68.9973 90.984 71.2517 91.46 73.7305 91.46C76.4192 91.46 78.8012 90.8972 80.8765 89.7716C80.978 89.7166 81.0788 89.6602 81.1788 89.6024C81.6498 89.3332 82.0975 89.0341 82.522 88.7052C84.0311 87.5359 85.2466 85.9894 86.1685 84.0657L98.1003 84.0657C97.0317 87.2785 95.4697 90.0629 93.4142 92.4188C92.1313 93.8892 90.6561 95.1928 88.9887 96.3294C87.9859 97.0131 86.9443 97.6177 85.8642 98.1433C82.2712 99.8918 78.2507 100.766 73.8028 100.766C69.9378 100.766 66.3656 100.075 63.0859 98.6928C62.1827 98.3121 61.3016 97.879 60.4427 97.3935C58.6731 96.3985 57.0693 95.2274 55.6314 93.8801C53.8374 92.1993 52.3016 90.2444 51.0238 88.0153C50.6545 87.3678 50.315 86.7066 50.0053 86.0318Z" fill="url(#paint0_linear_33_1726)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.986 85.9897C49.9924 86.0038 49.9989 86.0178 50.0053 86.0318C50.315 86.7066 50.6545 87.3678 51.0238 88.0153C52.2915 90.2268 53.8133 92.1685 55.5891 93.8404C55.6032 93.8536 55.6173 93.8669 55.6314 93.8801C57.0693 95.2274 58.6731 96.3985 60.4427 97.3935C61.289 97.8718 62.1567 98.2993 63.046 98.6759C63.0593 98.6815 63.0726 98.6871 63.0859 98.6928C66.3656 100.075 69.9378 100.766 73.8028 100.766C78.2507 100.766 82.2712 99.8918 85.8642 98.1433C86.9443 97.6177 87.9859 97.0131 88.9887 96.3294C90.6561 95.1928 92.1313 93.8892 93.4142 92.4188C95.219 90.3502 96.6434 87.9512 97.6874 85.222C97.8324 84.8429 97.97 84.4575 98.1003 84.0657L86.1685 84.0657C85.251 85.9804 84.0425 87.5214 82.5432 88.6888C82.5361 88.6943 82.5291 88.6997 82.522 88.7052C82.0975 89.0341 81.6498 89.3332 81.1788 89.6024C81.0788 89.6602 80.978 89.7166 80.8765 89.7716C78.8012 90.8972 76.4192 91.46 73.7305 91.46C71.2517 91.46 68.9973 90.984 66.9671 90.032C66.5295 89.8267 66.1023 89.5994 65.6855 89.3499C64.9377 88.9 64.2424 88.3911 63.5994 87.8232C62.2318 86.6153 61.1013 85.1403 60.2077 83.3984C58.9061 80.8375 58.2552 77.8377 58.2552 74.399C58.2552 70.9964 58.9061 68.0026 60.2077 65.4176C61.1238 63.615 62.289 62.0988 63.7033 60.8692C63.7117 60.8619 63.72 60.8547 63.7284 60.8474C64.3351 60.3225 64.9875 59.85 65.6855 59.4301C66.1023 59.1806 66.5295 58.9532 66.9671 58.748C68.9973 57.796 71.2517 57.32 73.7305 57.32C76.3793 57.32 78.7304 57.8715 80.7839 58.9745C80.9168 59.0459 81.0484 59.1196 81.1788 59.1956C81.668 59.4835 82.1322 59.8027 82.5713 60.1531C84.0576 61.3393 85.2567 62.8837 86.1685 64.7864L98.1003 64.7864C97.9712 64.3945 97.8349 64.0091 97.6914 63.6302C96.5838 60.7035 95.0501 58.162 93.0906 56.0057C91.8942 54.6891 90.5389 53.516 89.0249 52.4866C88.1994 51.9254 87.3461 51.4178 86.465 50.9639C82.7408 49.0454 78.5201 48.0861 73.8028 48.0861C69.9025 48.0861 66.3003 48.7861 62.9961 50.1861C62.1243 50.5555 61.2731 50.9737 60.4427 51.4406C58.5939 52.4858 56.926 53.7193 55.4392 55.1412C55.425 55.1546 55.4109 55.1682 55.3969 55.1817C53.7032 56.8092 52.2455 58.6822 51.0238 60.8007C50.7015 61.3624 50.402 61.9349 50.1251 62.518C50.1184 62.5321 50.1117 62.5461 50.1051 62.5602C48.4276 66.1101 47.5889 70.0564 47.5889 74.399C47.5889 78.6276 48.3879 82.4912 49.986 85.9897ZM96.4511 63.6302L86.8789 63.6302C85.9803 61.9478 84.8518 60.542 83.4933 59.4129C83.4269 59.3577 83.36 59.3032 83.2925 59.2494C83.2752 59.2355 83.2579 59.2218 83.2405 59.208C82.774 58.8397 82.2823 58.5034 81.7652 58.1991C81.6204 58.1147 81.4756 58.0336 81.331 57.9559C81.2673 57.9217 81.2034 57.888 81.1392 57.8548C78.9614 56.7274 76.4919 56.1637 73.7305 56.1637C71.1522 56.1637 68.7939 56.6486 66.6556 57.6184C66.5956 57.6456 66.5358 57.6732 66.4762 57.7012C66.4528 57.7121 66.4295 57.7231 66.4062 57.7342C65.9578 57.9472 65.5196 58.1818 65.0917 58.438C64.3353 58.893 63.6287 59.4047 62.9719 59.9731C62.9118 60.025 62.8522 60.0774 62.7931 60.1302C61.3337 61.4338 60.1284 63.0217 59.1769 64.8938C57.7916 67.6449 57.0989 70.8133 57.0989 74.399C57.0989 78.0223 57.7916 81.1967 59.177 83.9223C60.1092 85.7397 61.2838 87.2892 62.7008 88.5708C62.7449 88.6107 62.7893 88.6504 62.8339 88.6898C62.8514 88.7053 62.869 88.7207 62.8866 88.7361C63.5675 89.3319 64.3018 89.8668 65.0894 90.3407C65.5411 90.611 66.0034 90.8571 66.4762 91.0788C66.5357 91.1067 66.5954 91.1343 66.6553 91.1614C68.7937 92.1313 71.1521 92.6162 73.7305 92.6162C76.5326 92.6162 79.0341 92.041 81.235 90.8906C81.2995 90.8569 81.3637 90.8227 81.4277 90.788C81.5381 90.7281 81.6479 90.6667 81.7568 90.6038C82.113 90.4002 82.4577 90.1805 82.7909 89.9446C82.9396 89.8394 83.086 89.7309 83.2302 89.6192C83.2995 89.5655 83.3683 89.5111 83.4364 89.4559C84.8202 88.3369 85.9676 86.9256 86.8787 85.222L96.4453 85.222C95.4668 87.6528 94.166 89.7983 92.5429 91.6587C91.325 93.0546 89.9232 94.2931 88.3375 95.374C87.381 96.0261 86.3879 96.6026 85.3583 97.1036C81.925 98.7744 78.0731 99.6097 73.8028 99.6097C70.0936 99.6097 66.671 98.9489 63.535 97.6273C62.6728 97.2639 61.8318 96.8505 61.0117 96.3869C59.3218 95.4367 57.7919 94.3199 56.4219 93.0363C54.7123 91.4345 53.2473 89.5692 52.0269 87.4403C51.7719 86.9931 51.5316 86.5387 51.306 86.077C51.2206 85.9022 51.1373 85.7264 51.0561 85.5495C49.5155 82.1929 48.7451 78.4761 48.7451 74.399C48.7451 70.2129 49.5533 66.4179 51.1695 63.014C51.4336 62.4578 51.7194 61.9119 52.0267 61.3761C53.2026 59.3371 54.6064 57.5374 56.2383 55.9768C57.6556 54.6215 59.2467 53.4449 61.0117 52.4472C61.6777 52.0727 62.3583 51.7309 63.0533 51.4218C63.1841 51.3636 63.3154 51.3066 63.4472 51.2507C66.6073 49.9118 70.0591 49.2424 73.8028 49.2424C78.3332 49.2424 82.3774 50.1588 85.9355 51.9918C86.775 52.4242 87.588 52.9079 88.3748 53.4428C89.8132 54.4208 91.0999 55.5343 92.2349 56.7833C94.004 58.73 95.4094 61.0123 96.4511 63.6302Z" fill="url(#paint1_linear_33_1726)"/>
|
||||
<path d="M74 28.9065C98.9046 28.9065 119.094 49.0957 119.094 74.0002C119.094 98.9048 98.9046 119.094 74 119.094C49.0954 119.094 28.9062 98.9048 28.9062 74.0002C28.9062 49.0957 49.0954 28.9065 74 28.9065Z" stroke="url(#paint2_linear_33_1726)" stroke-width="9.25"/>
|
||||
<path d="M121.958 45.2277C132.691 45.773 140.04 48.7024 141.956 54.062C146.443 66.6128 119.449 87.7382 81.6651 101.246C43.8805 114.755 9.61256 115.531 5.12554 102.98C2.94303 96.8752 8.20767 88.7424 18.5402 80.4437" stroke="url(#paint3_linear_33_1726)" stroke-width="4.625" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_33_1726" x1="47.5889" y1="100.766" x2="98.1003" y2="48.0861" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_33_1726" x1="47.5889" y1="100.766" x2="98.1003" y2="48.0861" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_33_1726" x1="24.2813" y1="123.719" x2="123.719" y2="24.2815" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_33_1726" x1="-0.498047" y1="87.25" x2="150.08" y2="76.7875" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_33_1726">
|
||||
<rect width="148" height="148" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
|
|
@ -0,0 +1,11 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_936)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4H24C26.2091 4 28 5.79086 28 8V24C28 26.2091 26.2091 28 24 28H8C5.79086 28 4 26.2091 4 24V8C4 5.79086 5.79086 4 8 4Z" fill="#484151"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3333 14.6667V11.3333C17.3333 10.597 16.7364 10 16 10C15.2636 10 14.6667 10.597 14.6667 11.3333V14.6667H11.3333C10.597 14.6667 10 15.2636 10 16C10 16.7364 10.597 17.3333 11.3333 17.3333H14.6667V20.6667C14.6667 21.4031 15.2636 22 16 22C16.7364 22 17.3333 21.4031 17.3333 20.6667V17.3333H20.6667C21.4031 17.3333 22 16.7364 22 16C22 15.2636 21.4031 14.6667 20.6667 14.6667H17.3333Z" fill="#847D8B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_164_936">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 881 B |
|
|
@ -0,0 +1,19 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_979)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4H24C26.2091 4 28 5.79086 28 8V24C28 26.2091 26.2091 28 24 28H8C5.79086 28 4 26.2091 4 24V8C4 5.79086 5.79086 4 8 4Z" fill="url(#paint0_linear_164_979)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3333 14.6667V11.3333C17.3333 10.597 16.7364 10 16 10C15.2636 10 14.6667 10.597 14.6667 11.3333V14.6667H11.3333C10.597 14.6667 10 15.2636 10 16C10 16.7364 10.597 17.3333 11.3333 17.3333H14.6667V20.6667C14.6667 21.4031 15.2636 22 16 22C16.7364 22 17.3333 21.4031 17.3333 20.6667V17.3333H20.6667C21.4031 17.3333 22 16.7364 22 16C22 15.2636 21.4031 14.6667 20.6667 14.6667H17.3333Z" fill="url(#paint1_radial_164_979)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_164_979" x1="4" y1="16" x2="28" y2="16" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint1_radial_164_979" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12.3581 10) rotate(49.7215) scale(18.5614 18.5017)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_164_979">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1,32 @@
|
|||
<svg width="396" height="396" viewBox="0 0 396 396" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1096_17182)">
|
||||
<g filter="url(#filter0_di_1096_17182)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M260.655 65.7013C237.971 65.7013 215.321 74.4873 198.014 92.0934C180.707 74.4873 158.057 65.7013 135.373 65.7013C112.69 65.7013 90.0394 74.4873 72.7325 92.0934C38.1523 127.272 38.1523 184.296 72.7325 219.508C101.429 248.696 169.647 306.676 198.014 330.301C226.347 306.676 294.596 248.631 323.262 219.475C357.842 184.296 357.842 127.272 323.262 92.0595C305.989 74.4873 283.339 65.7013 260.655 65.7013Z" fill="url(#paint0_radial_1096_17182)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_di_1096_17182" x="-7.20264" y="11.7013" width="410.4" height="372.6" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="27"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.901961 0 0 0 0 0.235294 0 0 0 0 0.545098 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1096_17182"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1096_17182" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="18"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.542157 0 0 0 0 0.862647 0 0 0 0.72 0"/>
|
||||
<feBlend mode="lighten" in2="shape" result="effect2_innerShadow_1096_17182"/>
|
||||
</filter>
|
||||
<radialGradient id="paint0_radial_1096_17182" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(150.747 146.026) rotate(51.1886) scale(278.935 741.172)">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_1096_17182">
|
||||
<rect width="396" height="396" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M24 22C24.5523 22 25 22.4477 25 23C25 23.5523 24.5523 24 24 24H8C7.44772 24 7 23.5523 7 23C7 22.4477 7.44772 22 8 22H24ZM25 16L21 19V17H8C7.44772 17 7 16.5523 7 16C7 15.4477 7.44772 15 8 15H21V13L25 16ZM24 8C24.5523 8 25 8.44772 25 9C25 9.55228 24.5523 10 24 10H8C7.44772 10 7 9.55228 7 9C7 8.44772 7.44772 8 8 8H24Z" fill="#958E9E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 447 B |
|
|
@ -0,0 +1,13 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_982)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.7729 22.9651C27.1751 21.0012 28 18.5969 28 16C28 9.37258 22.6274 4 16 4C9.37258 4 4 9.37258 4 16C4 22.6274 9.37258 28 16 28C18.5937 28 20.9952 27.1771 22.9577 25.7782C22.5042 25.1743 22.6806 24.1646 23.3976 23.4307C24.1299 22.6812 25.1629 22.4886 25.7729 22.9651Z" fill="#5C5565"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.5C14 11.8807 12.8807 13 11.5 13C10.1193 13 9 11.8807 9 10.5C9 9.11929 10.1193 8 11.5 8C12.8807 8 14 9.11929 14 10.5Z" fill="#847D8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 16.5C11 17.3284 10.3284 18 9.5 18C8.67157 18 8 17.3284 8 16.5C8 15.6716 8.67157 15 9.5 15C10.3284 15 11 15.6716 11 16.5Z" fill="#847D8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.21523 18.2748C4.10475 17.6991 4.03542 17.1087 4.01041 16.5069C1.19344 18.2155 -0.352616 20.0249 0.068487 21.496C0.878956 24.3273 8.66928 24.8334 17.4687 22.6263C26.268 20.4192 32.7443 16.3347 31.9339 13.5034C31.5609 12.2003 29.7094 11.3897 26.9614 11.1096C27.181 11.6008 27.3684 12.1096 27.5212 12.6331C28.7042 12.902 29.4634 13.3596 29.6473 14.0022C30.2367 16.0613 24.6958 19.2403 17.2713 21.1025C9.84686 22.9647 3.35029 22.8051 2.76086 20.746C2.55441 20.0247 3.10002 19.1661 4.21523 18.2748Z" fill="#352E3E"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_164_982">
|
||||
<rect width="32.0025" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,24 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_1032)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.7729 22.9651C27.1751 21.0012 28 18.5969 28 16C28 9.37258 22.6274 4 16 4C9.37258 4 4 9.37258 4 16C4 22.6274 9.37258 28 16 28C18.5937 28 20.9952 27.1771 22.9577 25.7782C22.5042 25.1743 22.6806 24.1646 23.3976 23.4307C24.1299 22.6812 25.1629 22.4886 25.7729 22.9651Z" fill="url(#paint0_linear_164_1032)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 13C12.8807 13 14 11.8807 14 10.5C14 9.11929 12.8807 8 11.5 8C10.1193 8 9 9.11929 9 10.5C9 11.8807 10.1193 13 11.5 13ZM9.5 18C10.3284 18 11 17.3284 11 16.5C11 15.6716 10.3284 15 9.5 15C8.67157 15 8 15.6716 8 16.5C8 17.3284 8.67157 18 9.5 18Z" fill="url(#paint1_radial_164_1032)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.21523 18.2748C4.10475 17.6991 4.03542 17.1087 4.01041 16.5069C1.19344 18.2155 -0.352616 20.0249 0.068487 21.496C0.878956 24.3273 8.66928 24.8334 17.4687 22.6263C26.268 20.4192 32.7443 16.3347 31.9339 13.5034C31.5609 12.2003 29.7094 11.3897 26.9614 11.1096C27.181 11.6008 27.3684 12.1096 27.5212 12.6331C28.7042 12.902 29.4634 13.3596 29.6473 14.0022C30.2367 16.0613 24.6958 19.2403 17.2713 21.1025C9.84686 22.9647 3.35029 22.8051 2.76086 20.746C2.55441 20.0247 3.10002 19.1661 4.21523 18.2748Z" fill="url(#paint2_radial_164_1032)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_164_1032" x1="4" y1="16" x2="28" y2="16" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint1_radial_164_1032" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.17906 8) rotate(63.049) scale(13.2384 13.0672)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint2_radial_164_1032" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(6.28879 11.1096) rotate(25.4231) scale(35.4337 35.9556)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_164_1032">
|
||||
<rect width="32.0025" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1,14 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0V0C12.4183 0 16 3.58172 16 8V8C16 12.4183 12.4183 16 8 16V16C3.58172 16 0 12.4183 0 8V8Z" fill="#E12A2A"/>
|
||||
<mask id="mask0_0_7397" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0V0C12.4183 0 16 3.58172 16 8V8C16 12.4183 12.4183 16 8 16V16C3.58172 16 0 12.4183 0 8V8Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_0_7397)">
|
||||
<mask id="mask1_0_7397" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="2" y="2" width="12" height="12">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2H14V14H2V2Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_0_7397)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.55631 10.39H5.38631V11.26H7.55631V13H8.42631V11.26H10.6063V10.39H8.42631V9.49C10.1288 9.26265 11.3611 7.75374 11.2463 6.04C11.1316 4.32626 9.71388 3 7.99631 3C6.28101 3 4.86092 4.32853 4.74631 6.04C4.63171 7.75147 5.85656 9.25957 7.55631 9.49V10.39ZM10.2363 6.3125C10.2363 7.52062 9.25693 8.5 8.04881 8.5C6.84069 8.5 5.86131 7.52062 5.86131 6.3125C5.86131 5.10438 6.84069 4.125 8.04881 4.125C9.25693 4.125 10.2363 5.10438 10.2363 6.3125Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M24 22C24.5523 22 25 22.4477 25 23C25 23.5523 24.5523 24 24 24H8C7.44772 24 7 23.5523 7 23C7 22.4477 7.44772 22 8 22H24ZM11 15H24C24.5523 15 25 15.4477 25 16C25 16.5523 24.5523 17 24 17H11V19L7 16L11 13V15ZM24 8C24.5523 8 25 8.44772 25 9C25 9.55228 24.5523 10 24 10H8C7.44772 10 7 9.55228 7 9C7 8.44772 7.44772 8 8 8H24Z" fill="#958E9E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 451 B |
|
|
@ -0,0 +1,12 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_976)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3H19C21.2091 3 23 4.79086 23 7V25C23 27.2091 21.2091 29 19 29H5C2.79086 29 1 27.2091 1 25V7C1 4.79086 2.79086 3 5 3Z" fill="#484151"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.1153 5.25437L14.5927 1.63117C13.0085 1.20671 11.3916 1.79536 10.4297 2.99997H18.9998C21.2089 2.99997 22.9998 4.79083 22.9998 6.99997V25C22.9998 27.2091 21.2089 29 18.9998 29H16.2816L21.3865 30.3678C23.5203 30.9395 25.7136 29.6732 26.2853 27.5394L30.9437 10.1532C31.5154 8.01936 30.2491 5.82609 28.1153 5.25437Z" fill="#352E3E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2454 17.46L16.3273 16.28C17.2236 15.3017 17.2236 13.7083 16.3273 12.73C15.431 11.7517 13.9781 11.7517 13.0818 12.73L12 13.91L10.9182 12.73C10.0219 11.7517 8.56902 11.7517 7.67272 12.73C6.77641 13.7083 6.77642 15.3017 7.67272 16.28L12 21L15.2454 17.46Z" fill="#847D8B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_164_976">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,24 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_164_933)">
|
||||
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M29.0342 5.25489L15.5116 1.63169C13.9274 1.20723 12.3105 1.79589 11.3486 3.00049H19.9187C22.1278 3.00049 23.9187 4.79135 23.9187 7.00049V25.0005C23.9187 27.2096 22.1278 29.0005 19.9187 29.0005H17.2005L22.3054 30.3683C24.4392 30.94 26.6325 29.6737 27.2042 27.5399L31.8626 10.1537C32.4343 8.01988 31.168 5.82661 29.0342 5.25489Z" fill="url(#paint0_linear_164_933)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.91895 3.00052H19.9189C22.128 3.00052 23.9189 4.79139 23.9189 7.00052V25.0005C23.9189 27.2097 22.128 29.0005 19.9189 29.0005H5.91895C3.70981 29.0005 1.91895 27.2097 1.91895 25.0005V7.00052C1.91895 4.79139 3.70981 3.00052 5.91895 3.00052Z" fill="url(#paint1_linear_164_933)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.1644 17.4605L17.2462 16.2805C18.1425 15.3022 18.1425 13.7088 17.2462 12.7305C16.3499 11.7522 14.897 11.7522 14.0007 12.7305L12.9189 13.9105L11.8371 12.7305C10.9408 11.7522 9.48797 11.7522 8.59167 12.7305C7.69536 13.7088 7.69536 15.3022 8.59167 16.2805L12.9189 21.0005L16.1644 17.4605Z" fill="url(#paint2_radial_164_933)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_164_933" x1="11.3487" y1="1.49442" x2="31.9999" y2="30.5056" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_164_933" x1="1.91894" y1="16.0005" x2="23.9189" y2="16.0005" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint2_radial_164_933" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.88434 11.9968) rotate(46.7383) scale(14.59 14.5775)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F78AFF" stop-opacity="0.0117647"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_164_933">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z" fill="#00AD96"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.89976 4.50007L10.0435 5.16036L9.59348 5.93978L8.45 5.27961V6.03353C9.89336 6.25064 11 7.49609 11 9C11 10.6568 9.65684 12 8 12C6.34315 12 5 10.6568 5 9C5 7.49609 6.10662 6.25064 7.55 6.03353V5.27936L6.40625 5.93971L5.95625 5.16029L7.09979 4.50007L5.95615 3.83978L6.40615 3.06036L7.55 3.72076V3H8.45V3.72052L9.59354 3.06029L10.0435 3.83971L8.89976 4.50007ZM8.48059 11.0453C8.32467 11.0818 8.16447 11.1 8 11.1C7.83554 11.1 7.67535 11.0818 7.51943 11.0453C7.40508 11.0185 7.29261 10.9817 7.18287 10.9354C7.06836 10.8869 6.95979 10.8293 6.85714 10.7627C6.73473 10.6831 6.62061 10.5905 6.51508 10.4849C6.40951 10.3793 6.31694 10.2653 6.23737 10.1429C6.17071 10.0403 6.11307 9.93156 6.06466 9.81714C6.01819 9.70728 5.98156 9.59508 5.95476 9.48066C5.91826 9.32472 5.9 9.16446 5.9 9C5.9 8.83554 5.91825 8.67534 5.95474 8.51946C5.98154 8.40498 6.01818 8.29272 6.06466 8.18286C6.11309 8.06838 6.17065 7.95978 6.23734 7.85712C6.31692 7.73468 6.4095 7.62065 6.51508 7.51508C6.62065 7.4095 6.73468 7.31692 6.85716 7.23734C6.95979 7.17065 7.06836 7.11309 7.18287 7.06466C7.29274 7.01818 7.40493 6.98155 7.51941 6.95475C7.67531 6.91826 7.83556 6.9 8 6.9C8.16444 6.9 8.3246 6.91824 8.4805 6.95473C8.59498 6.98153 8.70728 7.01818 8.81714 7.06466C8.93162 7.11309 9.04022 7.17065 9.14282 7.23734C9.26534 7.31692 9.37934 7.4095 9.48494 7.51508C9.59042 7.6206 9.683 7.73456 9.7625 7.85694C9.82916 7.9596 9.88694 8.06838 9.93536 8.18286C9.9818 8.29272 10.0185 8.40492 10.0452 8.5194C10.0816 8.67522 10.1 8.83566 10.1 9C10.1 9.16446 10.0818 9.3246 10.0453 9.48054C10.0185 9.59508 9.9818 9.70722 9.93536 9.81714C9.88694 9.93156 9.8294 10.04 9.7628 10.1426C9.68324 10.265 9.59042 10.3794 9.48494 10.4849C9.37934 10.5905 9.26528 10.6831 9.14282 10.7627C9.04022 10.8293 8.93162 10.8869 8.81714 10.9354C8.70728 10.9818 8.59508 11.0185 8.48059 11.0453Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="25" height="22" viewBox="0 0 25 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.02862 22C10.7286 20.1478 21.5325 15.3511 23.5325 12.5016C25.5338 9.65215 26.0238 3.00329 21.5225 0.628706C17.0212 -1.74588 14.5274 3.00329 12.5299 8.22739C11.0149 0.628694 4.00984 -1.26474 0.506056 3.96263C-2.99897 9.19 13.0299 16.301 8.02862 22Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 418 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.48641 0.75C7.58626 0.75 6.68745 1.09865 6.00067 1.79731C5.31389 1.09865 4.41507 0.75 3.51492 0.75C2.61477 0.75 1.71595 1.09865 1.02917 1.79731C-0.343057 3.19327 -0.343057 5.45615 1.02917 6.85346L4.89515 10.7856C5.20053 11.0952 5.6006 11.25 6.00067 11.25C6.40073 11.25 6.8008 11.0952 7.10485 10.7842L10.9708 6.85212C12.3431 5.45615 12.3431 3.19327 10.9708 1.79596C10.2854 1.09865 9.38657 0.75 8.48641 0.75Z" fill="url(#paint0_linear_3450_4990)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3450_4990" x1="12" y1="6" x2="6.81573e-08" y2="6" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 819 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.72732 5.48254C9.08814 5.1255 9.57526 4.92523 10.0829 4.92523C10.5905 4.92523 11.0776 5.1255 11.4384 5.48254C11.6163 5.65858 11.7576 5.86816 11.854 6.09914C11.9504 6.33013 12 6.57793 12 6.82822C12 7.07852 11.9504 7.32632 11.854 7.55731C11.7576 7.78829 11.6163 7.99787 11.4384 8.17391L8.53375 11.0578L5.62908 8.17391C5.45116 7.99787 5.30992 7.78829 5.21353 7.55731C5.11713 7.32632 5.0675 7.07852 5.0675 6.82822C5.0675 6.57793 5.11713 6.33013 5.21353 6.09914C5.30992 5.86816 5.45116 5.65858 5.62908 5.48254C5.9899 5.1255 6.47702 4.92523 6.98463 4.92523C7.49224 4.92523 7.97936 5.1255 8.34018 5.48254L8.53375 5.67452L8.72732 5.48254ZM9.72985 2.1294C10.2125 2.61128 10.5282 3.2351 10.6305 3.90943C9.90862 3.77495 9.16247 3.91026 8.53375 4.28964C7.96277 3.94493 7.29307 3.80064 6.6308 3.87966C5.96852 3.95868 5.35157 4.25647 4.87772 4.72585C4.33349 5.26499 4.01951 5.99415 4.00181 6.76002C3.9841 7.52588 4.26406 8.26877 4.7828 8.83248L4.87772 8.93114L6.16874 10.2131L5.33419 11.0498L0.812142 6.52133C0.26782 5.91126 -0.0223019 5.11596 0.00133917 4.2987C0.0249803 3.48144 0.36059 2.70423 0.939268 2.12665C1.51795 1.54906 2.29578 1.21492 3.11309 1.19282C3.93039 1.17072 4.72515 1.46235 5.33419 2.00782C5.94347 1.4624 6.73852 1.17106 7.55596 1.19368C8.37339 1.21629 9.15112 1.55114 9.72932 2.1294H9.72985Z" fill="url(#paint0_linear_560_21739)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_560_21739" x1="12" y1="6.12471" x2="6.81573e-08" y2="6.12471" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
|
@ -0,0 +1,62 @@
|
|||
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_28_1180)">
|
||||
<g filter="url(#filter0_f_28_1180)">
|
||||
<path opacity="0.7" d="M47.9998 78.7533C64.9844 78.7533 78.7531 64.9846 78.7531 48.0001C78.7531 31.0155 64.9844 17.2468 47.9998 17.2468C31.0153 17.2468 17.2466 31.0155 17.2466 48.0001C17.2466 64.9846 31.0153 78.7533 47.9998 78.7533Z" fill="url(#paint0_linear_28_1180)"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_f_28_1180)">
|
||||
<path opacity="0.3" fill-rule="evenodd" clip-rule="evenodd" d="M20.9019 50.1481C20.9019 34.6784 33.1853 16.4155 48.6579 16.4155C64.1302 16.4155 73.9201 34.6784 73.9201 50.1481C73.9201 65.6174 67.5026 78.4008 52.03 78.4008C36.5578 78.4008 20.9019 65.6174 20.9019 50.1481Z" fill="url(#paint1_linear_28_1180)"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_f_28_1180)">
|
||||
<path opacity="0.3" fill-rule="evenodd" clip-rule="evenodd" d="M25.4555 33.0883C35.1813 21.0571 56.2154 14.5748 68.247 24.3011C80.2786 34.0273 76.4091 54.3851 66.6833 66.4163C56.9575 78.4474 43.9301 84.3552 31.8986 74.629C19.8669 64.9027 15.7297 45.1193 25.4555 33.0883Z" fill="url(#paint2_linear_28_1180)"/>
|
||||
</g>
|
||||
<g filter="url(#filter3_f_28_1180)">
|
||||
<path opacity="0.3" fill-rule="evenodd" clip-rule="evenodd" d="M45.654 75.035C30.3007 73.1182 13.6962 58.6688 15.6127 43.3185C17.5291 27.9682 36.8674 20.5187 52.2207 22.4355C67.574 24.3523 79.4665 32.303 77.5501 47.6532C75.6337 63.0035 61.0072 76.9517 45.654 75.035Z" fill="url(#paint3_linear_28_1180)"/>
|
||||
</g>
|
||||
<g clip-path="url(#paint4_angular_28_1180_clip_path)" data-figma-skip-parse="true"><g transform="matrix(0 0.016 -0.016 0 48 48)"><foreignObject x="-1250" y="-1250" width="2500" height="2500"><div xmlns="http://www.w3.org/1999/xhtml" style="background:conic-gradient(from 90deg,rgba(255, 255, 255, 0.6647) 0deg,rgba(255, 255, 255, 0) 179.143deg,rgba(255, 255, 255, 1) 269.62deg,rgba(255, 255, 255, 0.6647) 360deg);height:100%;width:100%;opacity:1"></div></foreignObject></g></g><path d="M61.3035 56.8891L59.6406 55.778L61.3035 56.8891ZM54.1229 62.7821L54.8883 64.6298L54.1229 62.7821ZM44.8786 63.6926L44.4884 65.6541L44.8786 63.6926ZM36.6863 59.3137L35.2721 60.7279L36.6863 59.3137ZM32.3074 51.1214L34.269 50.7313L32.3074 51.1214ZM33.2179 41.8771L31.3702 41.1117L33.2179 41.8771ZM64 48H62C62 50.7689 61.1789 53.4757 59.6406 55.778L61.3035 56.8891L62.9665 58.0003C64.9443 55.0402 66 51.5601 66 48H64ZM61.3035 56.8891L59.6406 55.778C58.1022 58.0803 55.9157 59.8747 53.3576 60.9343L54.1229 62.7821L54.8883 64.6298C58.1774 63.2675 60.9886 60.9603 62.9665 58.0003L61.3035 56.8891ZM54.1229 62.7821L53.3576 60.9343C50.7994 61.9939 47.9845 62.2712 45.2687 61.731L44.8786 63.6926L44.4884 65.6541C47.98 66.3487 51.5992 65.9922 54.8883 64.6298L54.1229 62.7821ZM44.8786 63.6926L45.2687 61.731C42.553 61.1908 40.0584 59.8574 38.1005 57.8995L36.6863 59.3137L35.2721 60.7279C37.7894 63.2453 40.9967 64.9596 44.4884 65.6541L44.8786 63.6926ZM36.6863 59.3137L38.1005 57.8995C36.1426 55.9416 34.8092 53.447 34.269 50.7313L32.3074 51.1214L30.3459 51.5116C31.0404 55.0033 32.7547 58.2106 35.2721 60.7279L36.6863 59.3137ZM32.3074 51.1214L34.269 50.7313C33.7288 48.0155 34.0061 45.2006 35.0657 42.6424L33.2179 41.8771L31.3702 41.1117C30.0078 44.4008 29.6513 48.02 30.3459 51.5116L32.3074 51.1214ZM33.2179 41.8771L35.0657 42.6424C36.1253 40.0843 37.9197 37.8978 40.222 36.3594L39.1109 34.6965L37.9997 33.0335C35.0397 35.0114 32.7325 37.8226 31.3702 41.1117L33.2179 41.8771ZM39.1109 34.6965L40.222 36.3594C42.5243 34.8211 45.2311 34 48 34V32V30C44.4399 30 40.9598 31.0557 37.9997 33.0335L39.1109 34.6965Z" data-figma-gradient-fill="{"type":"GRADIENT_ANGULAR","stops":[{"color":{"r":1.0,"g":1.0,"b":1.0,"a":0.0},"position":0.49762049317359924},{"color":{"r":1.0,"g":1.0,"b":1.0,"a":1.0},"position":0.74894374608993530}],"stopsVar":[{"color":{"r":1.0,"g":1.0,"b":1.0,"a":0.0},"position":0.49762049317359924},{"color":{"r":1.0,"g":1.0,"b":1.0,"a":1.0},"position":0.74894374608993530}],"transform":{"m00":1.9594349641362686e-15,"m01":-32.0,"m02":64.0,"m10":32.0,"m11":1.9594349641362686e-15,"m12":32.0},"opacity":1.0,"blendMode":"NORMAL","visible":true}"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_28_1180" x="-4.91792" y="-4.91768" width="105.835" height="105.836" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="11.0823" result="effect1_foregroundBlur_28_1180"/>
|
||||
</filter>
|
||||
<filter id="filter1_f_28_1180" x="16.469" y="11.9826" width="61.8839" height="70.8512" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="2.21645" result="effect1_foregroundBlur_28_1180"/>
|
||||
</filter>
|
||||
<filter id="filter2_f_28_1180" x="18.9377" y="18.4741" width="57.9196" height="62.1381" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="0.554113" result="effect1_foregroundBlur_28_1180"/>
|
||||
</filter>
|
||||
<filter id="filter3_f_28_1180" x="14.3527" y="21.0314" width="64.5084" height="55.2887" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="0.554113" result="effect1_foregroundBlur_28_1180"/>
|
||||
</filter>
|
||||
<clipPath id="paint4_angular_28_1180_clip_path"><path d="M61.3035 56.8891L59.6406 55.778L61.3035 56.8891ZM54.1229 62.7821L54.8883 64.6298L54.1229 62.7821ZM44.8786 63.6926L44.4884 65.6541L44.8786 63.6926ZM36.6863 59.3137L35.2721 60.7279L36.6863 59.3137ZM32.3074 51.1214L34.269 50.7313L32.3074 51.1214ZM33.2179 41.8771L31.3702 41.1117L33.2179 41.8771ZM64 48H62C62 50.7689 61.1789 53.4757 59.6406 55.778L61.3035 56.8891L62.9665 58.0003C64.9443 55.0402 66 51.5601 66 48H64ZM61.3035 56.8891L59.6406 55.778C58.1022 58.0803 55.9157 59.8747 53.3576 60.9343L54.1229 62.7821L54.8883 64.6298C58.1774 63.2675 60.9886 60.9603 62.9665 58.0003L61.3035 56.8891ZM54.1229 62.7821L53.3576 60.9343C50.7994 61.9939 47.9845 62.2712 45.2687 61.731L44.8786 63.6926L44.4884 65.6541C47.98 66.3487 51.5992 65.9922 54.8883 64.6298L54.1229 62.7821ZM44.8786 63.6926L45.2687 61.731C42.553 61.1908 40.0584 59.8574 38.1005 57.8995L36.6863 59.3137L35.2721 60.7279C37.7894 63.2453 40.9967 64.9596 44.4884 65.6541L44.8786 63.6926ZM36.6863 59.3137L38.1005 57.8995C36.1426 55.9416 34.8092 53.447 34.269 50.7313L32.3074 51.1214L30.3459 51.5116C31.0404 55.0033 32.7547 58.2106 35.2721 60.7279L36.6863 59.3137ZM32.3074 51.1214L34.269 50.7313C33.7288 48.0155 34.0061 45.2006 35.0657 42.6424L33.2179 41.8771L31.3702 41.1117C30.0078 44.4008 29.6513 48.02 30.3459 51.5116L32.3074 51.1214ZM33.2179 41.8771L35.0657 42.6424C36.1253 40.0843 37.9197 37.8978 40.222 36.3594L39.1109 34.6965L37.9997 33.0335C35.0397 35.0114 32.7325 37.8226 31.3702 41.1117L33.2179 41.8771ZM39.1109 34.6965L40.222 36.3594C42.5243 34.8211 45.2311 34 48 34V32V30C44.4399 30 40.9598 31.0557 37.9997 33.0335L39.1109 34.6965Z"/></clipPath><linearGradient id="paint0_linear_28_1180" x1="22.8007" y1="58.131" x2="75.5646" y2="58.131" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5271C4"/>
|
||||
<stop offset="0.48" stop-color="#B19FFF"/>
|
||||
<stop offset="1" stop-color="#ECA1FE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_28_1180" x1="20.9019" y1="16.4155" x2="73.9199" y2="78.4006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_28_1180" x1="46.6636" y1="6.85327" x2="48.9202" y2="88.3894" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_28_1180" x1="8.43413" y1="56.9331" x2="89.6805" y2="36.1575" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9CB5"/>
|
||||
<stop offset="0.4903" stop-color="#BC97EF"/>
|
||||
<stop offset="1" stop-color="#8BE6F0" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_28_1180">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9729 1.50061C15.1727 1.50061 13.3751 2.19788 12.0016 3.59511C10.6282 2.19788 8.83062 1.50061 7.03042 1.50061C5.23022 1.50061 3.43269 2.19788 2.05921 3.59511C-0.6851 6.38688 -0.6851 10.9124 2.05921 13.7069L9.79073 21.5706C10.4015 22.1898 11.2016 22.4994 12.0016 22.4994C12.8017 22.4994 13.6018 22.1898 14.2099 21.568L21.9414 13.7042C24.6857 10.9124 24.6857 6.38688 21.9414 3.59242C20.5706 2.19788 18.7731 1.50061 16.9729 1.50061Z" fill="url(#paint0_radial_3402_1568)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_3402_1568" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(3.75077 3.00053) rotate(67.0679) scale(21.1722 24.1968)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 925 B |
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -0,0 +1,14 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0V0C12.4183 0 16 3.58172 16 8V8C16 12.4183 12.4183 16 8 16V16C3.58172 16 0 12.4183 0 8V8Z" fill="#4E48FF"/>
|
||||
<mask id="mask0_0_7389" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0V0C12.4183 0 16 3.58172 16 8V8C16 12.4183 12.4183 16 8 16V16C3.58172 16 0 12.4183 0 8V8Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_0_7389)">
|
||||
<mask id="mask1_0_7389" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="4" y="4" width="8" height="8">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4H12V12H4V4Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_0_7389)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.07647 8.16C3.57931 10.4126 5.5839 12.4172 7.83647 11.92C10.089 11.4228 11.0656 8.76404 9.66647 6.93L11.1565 5.44V7.37H11.9965V4H8.62647V4.84H10.5565L9.06647 6.33C7.23201 4.93087 4.57364 5.90743 4.07647 8.16ZM9.46648 8.84C9.46648 7.56137 8.4351 6.53 7.15647 6.53C5.87784 6.53 4.83647 7.56137 4.83647 8.84C4.83647 10.1186 5.87784 11.16 7.15647 11.16C8.4351 11.16 9.46648 10.1186 9.46648 8.84Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.02453 13C7.02453 8.58172 10.9993 5 15.9025 5C20.9939 5 24.7805 8.58172 24.7805 13V17.6238C24.7805 19.162 25.459 20.6544 26.5253 21.8555C27.6504 23.1228 26.6491 25 24.8481 25H7.15191C5.35092 25 4.34965 23.1228 5.47472 21.8555C6.54108 20.6544 7.02453 19.162 7.02453 17.6238V13Z" fill="#5C5565"/>
|
||||
<path d="M12.9463 5.02659C12.9463 3.35505 14.3013 2 15.9729 2C17.6444 2 18.9995 3.35505 18.9995 5.02659V5.55556C18.9995 5.55556 17.7022 6 15.9728 6C14.2434 6 12.9463 5.55556 12.9463 5.55556V5.02659Z" fill="#352E3E"/>
|
||||
<path d="M18.9995 27C18.9995 27.7956 18.6806 28.5587 18.113 29.1213C17.5454 29.6839 16.7756 30 15.9729 30C15.1702 30 14.4004 29.6839 13.8328 29.1213C13.2652 28.5587 12.9463 27.7956 12.9463 27L15.9729 27H18.9995Z" fill="#847D8B"/>
|
||||
<path d="M9.42432 14C9.42432 13.8565 9.42432 13.7027 9.42432 13.5408C9.42432 10.4812 11.9046 8 14.9642 8V8M9.42432 18V19" stroke="#847D8B" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 0C12.4114 0 16 3.58857 16 8C16 12.4114 12.4114 16 8 16C3.58857 16 0 12.4114 0 8C0 3.58857 3.58857 0 8 0ZM8 1.5C4.41723 1.5 1.5 4.41723 1.5 8C1.5 11.5828 4.41723 14.5 8 14.5C11.5828 14.5 14.5 11.5828 14.5 8C14.5 4.41723 11.5828 1.5 8 1.5ZM7.9502 11.4004C8.36432 11.4005 8.7002 11.7363 8.7002 12.1504C8.69998 12.5643 8.36419 12.9003 7.9502 12.9004C7.53612 12.9004 7.20041 12.5644 7.2002 12.1504C7.2002 11.7362 7.53599 11.4004 7.9502 11.4004ZM8.24023 3.17969C10.5557 3.30157 11.6132 5.53815 10.6699 7.34961C10.436 7.79859 10.1479 8.083 9.66992 8.42969L9.41016 8.61035C8.80728 9.03074 8.69572 9.20599 8.75 9.7998C8.78771 10.2122 8.48266 10.5725 8.07031 10.6104C7.65782 10.6481 7.28772 10.3422 7.25 9.92969C7.14125 8.73956 7.52702 8.12751 8.4502 7.45996L8.67969 7.29004C9.05142 7.02961 9.21583 6.86958 9.33008 6.65039C9.79708 5.75386 9.32683 4.73075 8.16992 4.66992C7.09056 4.61322 6.36865 5.73394 6.63965 6.33008L6.66016 6.37988C6.86929 6.73742 6.74719 7.20103 6.38965 7.41016C6.03216 7.61902 5.57918 7.49708 5.37012 7.13965C4.44105 5.55084 5.89606 3.13505 8.12988 3.16992L8.24023 3.17969Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1,21 @@
|
|||
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" fill="none" customFrame="url(#clipPath_3)">
|
||||
<defs>
|
||||
<clipPath id="clipPath_3">
|
||||
<rect width="20" height="20" x="0" y="0" rx="4" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
<clipPath id="clipPath_4">
|
||||
<rect width="20" height="20" x="0" y="0" rx="9.5" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
<clipPath id="clipPath_5">
|
||||
<rect width="12" height="12" x="4" y="4" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect id="Checkbox&Radiobtn" width="20" height="20" x="0" y="0" rx="4" />
|
||||
<g id="radiobtn" customFrame="url(#clipPath_4)">
|
||||
<rect id="radiobtn" width="20" height="20" x="0" y="0" rx="9.5" fill="rgb(210,31,119)" style="mix-blend-mode:normal" />
|
||||
<g id="icon/select" customFrame="url(#clipPath_5)">
|
||||
<rect id="icon/select" width="12" height="12" x="4" y="4" />
|
||||
<path id="路径 18" d="M15.0878 7.21296C15.242 7.05769 15.3286 6.84769 15.3286 6.6288C15.3286 6.17109 14.9576 5.80005 14.4999 5.80005C14.2471 5.80005 14.0081 5.91547 13.8508 6.11348L8.46446 12.1732L6.11757 9.82626C5.96035 9.6505 5.7357 9.55005 5.49989 9.55005C5.04219 9.55005 4.67114 9.92109 4.67114 10.3788C4.67114 10.6146 4.7716 10.8393 4.94735 10.9965L7.91478 13.9639C7.92624 13.9754 7.93804 13.9865 7.95015 13.9973C8.29172 14.3009 8.81474 14.2701 9.11835 13.9285L15.0878 7.21296Z" fill="rgb(255,255,255)" style="mix-blend-mode:normal" fill-rule="evenodd" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="103" height="103" viewBox="0 0 103 103" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="102.4" height="102.4" transform="translate(0 0.600098)" fill="url(#paint0_linear_3919_5071)"/>
|
||||
<path d="M40.0645 51.8015C41.2645 51.8015 42.4645 51.8015 43.6645 51.8015C43.632 49.1145 47.7291 48.6352 51.3793 49.2288C62.5194 51.1505 75.0313 59.8681 74.6556 69.84C74.9453 78.1086 58.8867 76.7781 49.2973 72.7267C39.0656 68.7706 28.6322 60.7117 28.6999 51.8015C28.6999 51.7142 28.7009 51.6271 28.7028 51.5401C28.8877 42.4847 39.4798 34.2448 49.8169 30.2969C59.7582 26.2323 75.029 25.3245 74.6724 34.9901C73.5739 42.8608 60.65 52.5412 50.2812 55.2143C48.5198 55.6024 46.6883 55.6747 45.5274 55.1714C44.4095 54.7313 43.7625 53.5991 43.6645 51.8015C42.4645 51.8015 41.2645 51.8015 40.0645 51.8015C39.8677 54.1203 40.8724 57.498 43.6102 58.8914C46.2002 60.2637 48.7696 60.1429 51.0506 59.8914C57.9762 58.861 63.8758 55.8349 69.3963 52.1511C74.5762 48.1449 80.8387 44.501 82.1274 34.9901C82.2971 31.2294 80.9135 26.5893 77.6418 23.6756C74.4329 20.738 70.5355 19.5986 66.9673 19.1047C59.7515 18.2263 53.0349 19.6023 46.6869 21.7907C34.6867 26.3839 20.464 34.4612 19.105 51.3305C19.1016 51.4872 19.0999 51.6442 19.0999 51.8015C20.0353 68.6678 34.3797 76.6767 46.1203 81.2188C52.3585 83.4165 58.8886 84.802 65.8918 84.3532C69.3588 84.0719 73.1292 83.3595 76.6192 80.9711C80.2118 78.6759 82.405 73.7893 82.1442 69.84C80.5697 52.9942 64.8862 46.0239 51.8709 44.5144C49.605 44.3843 47.2353 44.441 44.8091 45.3061C42.3968 45.976 39.7277 48.9216 40.0645 51.8015ZM43.6645 51.8015H40.0645H43.6645Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3919_5071" x1="102.4" y1="102.4" x2="1.2207e-05" y2="-1.2207e-05" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F264A4"/>
|
||||
<stop offset="1" stop-color="#C241E6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z" fill="#E12A2A"/>
|
||||
<mask id="mask0_1761_17942" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1761_17942)">
|
||||
<path d="M12 15C12.8284 15 13.5 15.6716 13.5 16.5C13.5 17.3284 12.8284 18 12 18C11.1716 18 10.5 17.3284 10.5 16.5C10.5 15.6716 11.1716 15 12 15ZM12 6C12.8284 6 13.5 6.61212 13.5 7.36523V12.1348C13.5 12.8879 12.8284 13.5 12 13.5C11.1716 13.5 10.5 12.8879 10.5 12.1348V7.36523C10.5 6.61212 11.1716 6 12 6Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 947 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z" fill="#00AD96"/>
|
||||
<mask id="mask0_40_567" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_40_567)">
|
||||
<path d="M5 8L7 10L11 6" stroke="white" stroke-width="2" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 692 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 21C21.8284 21 22.5 20.3284 22.5 19.5V7.5L17.25 13.5L12 3L6.75 13.5L1.5 7.5V19.5C1.5 20.3284 2.17157 21 3 21H21Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5 14C15.0523 14 15.5 13.5523 15.5 13V5L12 9L8.5 2L5 9L1.5 5V13C1.5 13.5523 1.94772 14 2.5 14H14.5Z" fill="url(#paint0_linear_51_3337)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_51_3337" x1="1.5" y1="7.98957" x2="15.5" y2="7.98957" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9696"/>
|
||||
<stop offset="0.5" stop-color="#AA90F9"/>
|
||||
<stop offset="1" stop-color="#8DF3E2"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 557 B |
|
|
@ -0,0 +1,10 @@
|
|||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 21 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="vip" fill-rule="evenodd" clip-rule="evenodd" d="M19.5 18C20.3284 18 21 17.3284 21 16.5V4.5L15.75 10.5L10.5 0L5.25 10.5L0 4.5V16.5C0 17.3284 0.671573 18 1.5 18H19.5Z" fill="url(#paint0_linear_3473_8531)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3473_8531" x1="-1.60639e-07" y1="8.98435" x2="21" y2="8.98435" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF9696"/>
|
||||
<stop offset="0.5" stop-color="#AA90F9"/>
|
||||
<stop offset="1" stop-color="#8DF3E2"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 559 KiB |
|
|
@ -0,0 +1,29 @@
|
|||
<svg width="125" height="120" viewBox="0 0 125 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.2" clip-path="url(#clip0_636_2720)">
|
||||
<path d="M50.295 111.176L31.878 26.9237L66.6193 9.75L126.375 44.25L128.873 82.9238L50.295 111.176Z" fill="url(#paint0_linear_636_2720)"/>
|
||||
<path d="M66.6195 9.75L126.375 44.25L128.995 82.7128L32 26.7128L66.6195 9.75Z" fill="url(#paint1_linear_636_2720)"/>
|
||||
<path d="M66.6196 9.75L126.375 44.25L50.4796 110.705L66.6196 9.75Z" fill="url(#paint2_linear_636_2720)"/>
|
||||
<path d="M61.0117 43.4628L96.9303 27.25L100.849 66.4628L50.4793 110.705L61.0117 43.4628Z" fill="url(#paint3_linear_636_2720)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_636_2720" x1="96.4972" y1="27" x2="48.4972" y2="110.138" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.3"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_636_2720" x1="80.4974" y1="54.7128" x2="96.4974" y2="27" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.3"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_636_2720" x1="48.964" y1="109.83" x2="96.714" y2="27.125" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.3"/>
|
||||
<stop offset="0.502232" stop-color="white" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_636_2720" x1="48.9303" y1="110.388" x2="96.9303" y2="27.25" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.459265" stop-color="white" stop-opacity="0.1"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.3"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_636_2720">
|
||||
<rect width="112" height="96" fill="white" transform="translate(48 -1) rotate(30)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="25" height="28" viewBox="0 0 25 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.5 0L16.4064 7.61705L22.5 10L16.4064 12.3829L14.5 20L12.5936 12.3829L6.5 10L12.5936 7.61705L14.5 0Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M6.5 18L7.45318 21.8085L10.5 23L7.45318 24.1915L6.5 28L5.54682 24.1915L2.5 23L5.54682 21.8085L6.5 18Z" fill="white" fill-opacity="0.85"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 398 B |
|
After Width: | Height: | Size: 692 KiB |
|
After Width: | Height: | Size: 848 KiB |
|
After Width: | Height: | Size: 387 KiB |
|
After Width: | Height: | Size: 974 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 459 KiB |
|
After Width: | Height: | Size: 669 KiB |
|
After Width: | Height: | Size: 475 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="148" height="112" viewBox="0 0 148 112" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0L74 43L148 0V112H0V0Z" fill="url(#paint0_linear_486_1272)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_486_1272" x1="74" y1="0" x2="74" y2="112" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFF87C" stop-opacity="0"/>
|
||||
<stop offset="0.745192" stop-color="#FFB940" stop-opacity="0.8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 434 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="110" height="112" viewBox="0 0 110 112" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0L55 43L110 0V112H0V0Z" fill="url(#paint0_linear_499_13275)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_499_13275" x1="55" y1="0" x2="55" y2="112" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#CBC1FF" stop-opacity="0"/>
|
||||
<stop offset="0.75" stop-color="#5740FF" stop-opacity="0.8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 432 B |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="110" height="112" viewBox="0 0 110 112" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<foreignObject x="0" y="0" width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(8px);clip-path:url(#bgblur_0_499_13273_clip_path);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="16" d="M-6.10352e-05 0L54.9999 43L110 0V112H-6.10352e-05V0Z" fill="url(#paint0_linear_499_13273)"/>
|
||||
<defs>
|
||||
<clipPath id="bgblur_0_499_13273_clip_path" transform="translate(0 0)"><path d="M-6.10352e-05 0L54.9999 43L110 0V112H-6.10352e-05V0Z"/>
|
||||
</clipPath><linearGradient id="paint0_linear_499_13273" x1="54.9999" y1="0" x2="54.9999" y2="112" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFC47C" stop-opacity="0"/>
|
||||
<stop offset="0.745192" stop-color="#FF9040" stop-opacity="0.8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 865 B |