diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d46e53b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# @see: http://editorconfig.org + +root = true + +[*] # 表示所有文件适用 +charset = utf-8 # 设置文件字符集为 utf-8 +end_of_line = lf # 控制换行类型(lf | cr | crlf) +insert_final_newline = true # 始终在文件末尾插入一个新行 +indent_style = space # 缩进风格(tab | space) +indent_size = 2 # 缩进大小 +max_line_length = 100 # 最大行长度 + +[*.md] # 表示仅 md 文件适用以下规则 +insert_final_newline = false # 关闭末尾新行插入 +max_line_length = off # 关闭最大行长度限制 +trim_trailing_whitespace = false # 关闭末尾空格修剪 diff --git a/.env b/.env deleted file mode 100644 index 38aee11..0000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -HTTPS=true \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index bd4006f..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - es2020: true, - node: true, - }, - extends: [ - "eslint:recommended", - "@typescript-eslint/recommended", - "plugin:react-hooks/recommended", - ], - ignorePatterns: ["dist", ".eslintrc.js", "node_modules"], - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - }, - plugins: ["react-refresh"], - rules: { - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], - "@typescript-eslint/no-explicit-any": "warn", - }, - overrides: [ - { - files: ["packages/**/*.ts", "packages/**/*.tsx"], - rules: { - "no-console": "warn", - }, - }, - { - files: ["projects/**/*.ts", "projects/**/*.tsx"], - rules: { - "no-console": "off", - }, - }, - ], -}; diff --git a/README.md b/README.md index 8cb3391..ea2c193 100644 --- a/README.md +++ b/README.md @@ -22,26 +22,13 @@ 2. 全局根字体大小断点(`src/index.css`) ```html -html { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-size: -100%; } /* 小屏幕设备(如手机)*/ @media (max-width: 600px) { html { font-size: -90%; /* 字体稍微小一点 */ } } /* 中等屏幕设备(如平板)*/ @media (min-width: -601px) and (max-width: 1024px) { html { font-size: 100%; /* 标准大小 */ } } /* -大屏幕设备(如桌面)*/ @media (min-width: 1025px) { html { font-size: 110%; /* -字体稍微大一点 */ } } +html { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-size: 100%; } /* 小屏幕设备(如手机)*/ @media (max-width: 600px) { html { font-size: 90%; /* 字体稍微小一点 */ } } /* 中等屏幕设备(如平板)*/ @media (min-width: 601px) and (max-width: 1024px) { html { font-size: 100%; /* 标准大小 */ } } /* 大屏幕设备(如桌面)*/ @media (min-width: 1025px) { html { font-size: 110%; /* 字体稍微大一点 */ } } ``` 3. 组件库全局配色(`src/index.css`) ```html -:root { --primary-color: #FFC300; } :root:root { --adm-color-primary: #FFC300; ---adm-color-success: #00b578; --adm-color-warning: #ff8f1f; --adm-color-danger: -#ff3141; --adm-color-white: #ffffff; --adm-color-text: #333333; ---adm-color-text-secondary: #666666; --adm-color-weak: #999999; ---adm-color-light: #cccccc; --adm-color-border: #eeeeee; --adm-color-box: -#f5f5f5; --adm-color-background: #ffffff; --adm-font-size-main: -var(--adm-font-size-5); --adm-font-family: -apple-system, blinkmacsystemfont, -'Helvetica Neue', helvetica, segoe ui, arial, roboto, 'PingFang SC', 'miui', -'Hiragino Sans GB', 'Microsoft Yahei', sans-serif; } +:root { --primary-color: #FFC300; } :root:root { --adm-color-primary: #FFC300; --adm-color-success: #00b578; --adm-color-warning: #ff8f1f; --adm-color-danger: #ff3141; --adm-color-white: #ffffff; --adm-color-text: #333333; --adm-color-text-secondary: #666666; --adm-color-weak: #999999; --adm-color-light: #cccccc; --adm-color-border: #eeeeee; --adm-color-box: #f5f5f5; --adm-color-background: #ffffff; --adm-font-size-main: var(--adm-font-size-5); --adm-font-family: -apple-system, blinkmacsystemfont, 'Helvetica Neue', helvetica, segoe ui, arial, roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif; } ``` 4. 修改语言 @@ -99,8 +86,7 @@ export const useFetchXXX = () => { // set the url const url = `/xxx/xxx`; // fetch the data - const [{ data, loading, error }, refetch] = - useAxios < Result < MockResult >> url; + const [{ data, loading, error }, refetch] = useAxios < Result < MockResult >> url; // to do something return { data, loading, error, refetch }; }; @@ -113,8 +99,7 @@ export const useFetchPageXXX = (page: number, size: number) => { // set the url const url = `/xxx/xxx?page=${page}&size=${size}`; // fetch the data - const [{ data, loading, error }, refetch] = - useAxios < Page < MockResult >> url; + const [{ data, loading, error }, refetch] = useAxios < Page < MockResult >> url; // to do something return { data, loading, error, refetch }; }; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..e138a1f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,111 @@ +// import antfu from "@antfu/eslint-config"; + +// export default antfu({ +// vue: false, +// react: true, // @eslint-react/eslint-plugin +// typescript: true, +// stylistic: true, +// formatters: true, // eslint-plugin-format 格式化 +// rules: { +// "react/no-array-index-key": "off", +// "react/no-children-to-array": "off", +// "jsdoc/check-alignment": "off", +// "react-hooks/exhaustive-deps": "off", +// }, +// }); +// eslint.config.js +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tsParser from "@typescript-eslint/parser"; +import tsPlugin from "@typescript-eslint/eslint-plugin"; + +export default [ + // 忽略文件 + { + ignores: [ + "dist/**", + "node_modules/**", + "packages/*/dist/**", + "projects/*/dist/**", + "**/*.config.js", + "**/*.config.ts", + ], + }, + + // 基础 JavaScript 配置 + { + files: ["**/*.{js,jsx,ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", + globals: { + ...globals.browser, + ...globals.es2020, + ...globals.node, + }, + }, + rules: { + ...js.configs.recommended.rules, + }, + }, + + // TypeScript 配置 + { + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + "@typescript-eslint": tsPlugin, + }, + rules: { + ...tsPlugin.configs.recommended.rules, + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_" }, + ], + "@typescript-eslint/no-explicit-any": "warn", + }, + }, + + // React 配置 + { + files: ["**/*.{jsx,tsx}"], + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + }, + + // 项目特定规则 + { + files: ["projects/**/*.{ts,tsx}"], + rules: { + "no-console": "off", + }, + }, + + // 包特定规则 + { + files: ["packages/**/*.{ts,tsx}"], + rules: { + "no-console": "warn", + }, + }, +]; diff --git a/index.html b/index.html deleted file mode 100644 index 57d34a6..0000000 --- a/index.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - tashow-h5 - - - -
- - - diff --git a/package.json b/package.json index 8e3566e..54b81e1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "@types/node": "^20.10.0", "@types/react-router-dom": "^5.3.3", "@uidotdev/usehooks": "^2.4.1", - "@vitejs/plugin-basic-ssl": "^2.1.0", + "@workspace/hooks": "workspace:*", + "@workspace/shared": "workspace:*", + "@workspace/utils": "workspace:*", "antd-mobile": "^5.33.0", "antd-mobile-icons": "^0.3.0", "axios": "^1.6.2", @@ -35,18 +37,30 @@ "zustand": "^4.4.6" }, "devDependencies": { - "@types/lodash.isequal": "^4.5.8", + "@eslint-react/eslint-plugin": "^1.22.1", + "@eslint/js": "^9.15.0", + "@react-dev-inspector/vite-plugin": "^2.0.1", "@types/react": "^18.3.24", "@types/react-dom": "^18.3.7", "@types/react-slick": "^0.23.12", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^9.34.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", + "@vitejs/plugin-basic-ssl": "^2.1.0", + "@vitejs/plugin-react-swc": "^3.7.2", + "eslint": "^9.15.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.12.0", "postcss-pxtorem": "^6.0.0", + "react-dev-inspector": "^2.0.1", "typescript": "^5.2.2", - "vite": "^5.0.0" + "vite": "^6.0.5", + "vite-plugin-checker": "^0.8.0" + }, + "simple-git-hooks": { + "pre-commit": "npx lint-staged" + }, + "lint-staged": { + "*": "eslint --fix" } } diff --git a/packages/hooks/index.ts b/packages/hooks/index.ts index e69de29..82371fd 100644 --- a/packages/hooks/index.ts +++ b/packages/hooks/index.ts @@ -0,0 +1,3 @@ +import useDocumentTitle from "./src/useDocumentTitle"; + +export { useDocumentTitle }; diff --git a/packages/hooks/package.json b/packages/hooks/package.json new file mode 100644 index 0000000..bb85f8e --- /dev/null +++ b/packages/hooks/package.json @@ -0,0 +1,16 @@ +{ + "name": "@workspace/hooks", + "version": "1.0.0", + "main": "./index.ts", + "types": "./index.ts", + "exports": { + ".": { + "import": "./index.ts", + "types": "./index.ts" + } + }, + "scripts": { + "lint": "eslint src --ext .ts,.tsx", + "type-check": "tsc --noEmit" + } +} diff --git a/packages/hooks/src/i18n.ts b/packages/hooks/src/i18n.ts deleted file mode 100644 index ac3cd8c..0000000 --- a/packages/hooks/src/i18n.ts +++ /dev/null @@ -1,12 +0,0 @@ -import zhCN from '@/locales/zh_CN.json' -import enUS from '@/locales/en_US.json' -import {useI18nStore} from '@/store/i18n'; - -export default function useI18n() { - const {lang} = useI18nStore(); - const locales = lang === 'en_US' ? enUS : zhCN; - - return (name: string) => { - return locales[name as keyof typeof locales] || name; - } -} diff --git a/packages/hooks/src/location.ts b/packages/hooks/src/location.ts deleted file mode 100644 index f549fc2..0000000 --- a/packages/hooks/src/location.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {useState, useEffect} from 'react'; -import useI18n from "@/hooks/i18n"; - -// 定义位置坐标的类型 -export interface Coordinates { - lat: number; - lng: number; -} - -// 定义返回的位置状态的类型 -export interface LocationState { - loaded: boolean; - coordinates: Coordinates | null; -} - -// 定义错误状态的类型 -export type ErrorState = string | null; - -export const useLocation = (): { location: LocationState; error: ErrorState } => { - const t = useI18n(); - - // 用于存储位置信息和加载状态的状态 - const [location, setLocation] = useState({ - loaded: false, - coordinates: null, - }); - - // 用于存储任何错误消息的状态 - const [error, setError] = useState(null); - - // 地理位置成功处理函数 - const onSuccess = (location: GeolocationPosition) => { - setLocation({ - loaded: true, - coordinates: { - lat: location.coords.latitude, - lng: location.coords.longitude, - }, - }); - }; - - // 地理位置错误处理函数 - const onError = (error: GeolocationPositionError) => { - setError(error.message); - }; - - // 使用 useEffect 在组件挂载时执行地理位置请求 - useEffect(() => { - // 检查浏览器是否支持地理位置 - if (!navigator.geolocation) { - setError(t('hooks.location.unsupported')); - return; - } - - // 请求用户的当前位置 - navigator.geolocation.getCurrentPosition(onSuccess, onError); - }, []); - - return {location, error}; -}; diff --git a/packages/hooks/src/useDocumentTitle.ts b/packages/hooks/src/useDocumentTitle.ts new file mode 100644 index 0000000..4d93565 --- /dev/null +++ b/packages/hooks/src/useDocumentTitle.ts @@ -0,0 +1,16 @@ +// hooks/useDocumentTitle.js +import { useEffect } from "react"; + +const useDocumentTitle = (title: string) => { + useEffect(() => { + if (title) { + document.title = title; + } + + // 组件卸载时可以恢复默认标题 + return () => { + document.title = "默认标题"; // 或者从配置中获取 + }; + }, [title]); +}; +export default useDocumentTitle; diff --git a/packages/shared/package.json b/packages/shared/package.json index 7dc2c1b..20768e3 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -12,12 +12,5 @@ "scripts": { "lint": "eslint src --ext .ts,.tsx", "type-check": "tsc --noEmit" - }, - "dependencies": { - "react": "^18.2.0", - "antd-mobile": "^5.34.0" - }, - "peerDependencies": { - "react": ">=18.0.0" } } diff --git a/src/view/archives/index.less b/packages/shared/src/error-page/errorBoundary/index.less similarity index 100% rename from src/view/archives/index.less rename to packages/shared/src/error-page/errorBoundary/index.less diff --git a/packages/shared/src/error-page/errorBoundary/index.tsx b/packages/shared/src/error-page/errorBoundary/index.tsx new file mode 100644 index 0000000..cb74b6f --- /dev/null +++ b/packages/shared/src/error-page/errorBoundary/index.tsx @@ -0,0 +1,49 @@ +// components/ErrorBoundary/index.tsx +import React, { Component, ReactNode } from "react"; +import { Result, Button } from "antd-mobile"; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error?: Error; +} + +class ErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: any) { + console.error("ErrorBoundary caught an error:", error, errorInfo); + } + + render() { + if (this.state.hasError) { + if (this.props.fallback) { + return this.props.fallback; + } + + return ( +
+ + +
+ ); + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/packages/shared/src/error-page/index.module.less b/packages/shared/src/error-page/index.module.less new file mode 100644 index 0000000..f0809ae --- /dev/null +++ b/packages/shared/src/error-page/index.module.less @@ -0,0 +1,20 @@ +// components/ErrorPages/ErrorPages.module.less +.errorPage { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + padding: 20px; + background-color: #f5f5f5; +} + +.actions { + display: flex; + gap: 16px; + margin-top: 24px; + + .button { + min-width: 100px; + } +} diff --git a/packages/shared/src/error-page/notFound/index.tsx b/packages/shared/src/error-page/notFound/index.tsx new file mode 100644 index 0000000..5ba31e7 --- /dev/null +++ b/packages/shared/src/error-page/notFound/index.tsx @@ -0,0 +1,25 @@ +// components/ErrorPages/NotFound.tsx +import React from "react"; +import { Button, Result } from "antd-mobile"; +import { useNavigate } from "react-router-dom"; +import styles from "../index.module.less"; + +const NotFound: React.FC = () => { + const navigate = useNavigate(); + + return ( +
+ +
+ + +
+
+ ); +}; + +export default NotFound; diff --git a/packages/shared/src/error-page/serverError/index.tsx b/packages/shared/src/error-page/serverError/index.tsx new file mode 100644 index 0000000..4f62a2f --- /dev/null +++ b/packages/shared/src/error-page/serverError/index.tsx @@ -0,0 +1,29 @@ +// components/ErrorPages/ServerError.tsx +import React from "react"; +import { Button, Result } from "antd-mobile"; +import { useNavigate } from "react-router-dom"; +import styles from "../index.module.less"; + +const ServerError: React.FC = () => { + const navigate = useNavigate(); + + const handleRefresh = () => { + window.location.reload(); + }; + + return ( +
+ +
+ + +
+
+ ); +}; + +export default ServerError; diff --git a/packages/shared/src/xpopup/index.tsx b/packages/shared/src/xpopup/index.tsx index 723cc11..5aca3bf 100644 --- a/packages/shared/src/xpopup/index.tsx +++ b/packages/shared/src/xpopup/index.tsx @@ -12,7 +12,7 @@ function XPopup(props: DefinedProps) { const { visible, title, children, onClose } = props; return ( - +

{title}

diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index aaef027..b5e6a05 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json @@ -1,10 +1,26 @@ { - "extends": "../../tsconfig.json", "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" - }, - "include": ["src/**/*"], - "exclude": ["node_modules"] + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "typeRoots": ["./types"] + } } diff --git a/packages/shared/types/global.d.ts b/packages/shared/types/global.d.ts new file mode 100644 index 0000000..0aaab19 --- /dev/null +++ b/packages/shared/types/global.d.ts @@ -0,0 +1,19 @@ +// src/types/global.d.ts +declare module "*.module.less" { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module "*.module.css" { + const classes: { readonly [key: string]: string }; + export default classes; +} + +// 其他资源文件 +declare module "*.png"; +declare module "*.jpg"; +declare module "*.jpeg"; +declare module "*.gif"; +declare module "*.svg"; +declare module "*.ico"; +declare module "*.webp"; diff --git a/src/view/home/component/index.less b/packages/styles/index.less similarity index 100% rename from src/view/home/component/index.less rename to packages/styles/index.less diff --git a/public/null.svg b/packages/styles/mixins.less similarity index 100% rename from public/null.svg rename to packages/styles/mixins.less diff --git a/packages/styles/variables.less b/packages/styles/variables.less new file mode 100644 index 0000000..367d32f --- /dev/null +++ b/packages/styles/variables.less @@ -0,0 +1,43 @@ +// variables.less +// 颜色变量 +@primary-color: #1890ff; +@secondary-color: #6c757d; +@success-color: #52c41a; +@warning-color: #faad14; +@error-color: #f5222d; +@white: #ffffff; +@black: #000000; +@gray-1: #f7f8fa; +@gray-2: #ebedf0; +@gray-3: #c8c9cc; +@gray-4: #969799; +@gray-5: #646566; + +// 尺寸变量 +@border-radius: 6px; +@border-radius-sm: 4px; +@border-radius-lg: 8px; + +// 动画变量 +@ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); +@ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19); +@ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); + +// 间距变量 +@spacing-xs: 4px; +@spacing-sm: 8px; +@spacing-md: 16px; +@spacing-lg: 24px; +@spacing-xl: 32px; + +// 字体变量 +@font-size-sm: 12px; +@font-size-base: 14px; +@font-size-lg: 16px; +@font-size-xl: 18px; +@font-size-xxl: 20px; + +// 阴影变量 +@box-shadow-base: 0 2px 8px fade(@black, 15%); +@box-shadow-card: 0 1px 2px -2px fade(@black, 16%), 0 3px 6px 0 fade(@black, 12%), + 0 5px 12px 4px fade(@black, 9%); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86c70e7..d131c54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,19 +13,25 @@ importers: version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/node': specifier: ^20.10.0 - version: 20.19.11 + version: 20.19.12 '@types/react-router-dom': specifier: ^5.3.3 version: 5.3.3 '@uidotdev/usehooks': specifier: ^2.4.1 version: 2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@vitejs/plugin-basic-ssl': - specifier: ^2.1.0 - version: 2.1.0(vite@5.4.19(@types/node@20.19.11)(less@4.4.1)) + '@workspace/hooks': + specifier: workspace:* + version: link:packages/hooks + '@workspace/shared': + specifier: workspace:* + version: link:packages/shared + '@workspace/utils': + specifier: workspace:* + version: link:packages/utils antd-mobile: specifier: ^5.33.0 - version: 5.39.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.40.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) antd-mobile-icons: specifier: ^0.3.0 version: 0.3.0 @@ -70,11 +76,17 @@ importers: version: 1.6.5 zustand: specifier: ^4.4.6 - version: 4.5.7(@types/react@18.3.24)(react@18.3.1) + version: 4.5.7(@types/react@18.3.24)(immer@9.0.21)(react@18.3.1) devDependencies: - '@types/lodash.isequal': - specifier: ^4.5.8 - version: 4.5.8 + '@eslint-react/eslint-plugin': + specifier: ^1.22.1 + version: 1.53.0(eslint@9.34.0)(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2) + '@eslint/js': + specifier: ^9.15.0 + version: 9.34.0 + '@react-dev-inspector/vite-plugin': + specifier: ^2.0.1 + version: 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) '@types/react': specifier: ^18.3.24 version: 18.3.24 @@ -85,158 +97,114 @@ importers: specifier: ^0.23.12 version: 0.23.13 '@typescript-eslint/eslint-plugin': - specifier: ^6.10.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2) + specifier: ^8.15.0 + version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2) '@typescript-eslint/parser': - specifier: ^6.10.0 - version: 6.21.0(eslint@9.34.0)(typescript@5.9.2) + specifier: ^8.15.0 + version: 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@vitejs/plugin-basic-ssl': + specifier: ^2.1.0 + version: 2.1.0(vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0)) '@vitejs/plugin-react-swc': - specifier: ^3.5.0 - version: 3.11.0(vite@5.4.19(@types/node@20.19.11)(less@4.4.1)) + specifier: ^3.7.2 + version: 3.11.0(vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0)) eslint: - specifier: ^9.34.0 + specifier: ^9.15.0 version: 9.34.0 eslint-plugin-react-hooks: - specifier: ^5.2.0 + specifier: ^5.0.0 version: 5.2.0(eslint@9.34.0) eslint-plugin-react-refresh: - specifier: ^0.4.20 + specifier: ^0.4.14 version: 0.4.20(eslint@9.34.0) + globals: + specifier: ^15.12.0 + version: 15.15.0 postcss-pxtorem: specifier: ^6.0.0 version: 6.1.0(postcss@8.5.6) + react-dev-inspector: + specifier: ^2.0.1 + version: 2.0.1(@types/react@18.3.24)(eslint@9.34.0)(react@18.3.1)(typescript@5.9.2)(webpack@5.101.3) typescript: specifier: ^5.2.2 version: 5.9.2 vite: - specifier: ^5.0.0 - version: 5.4.19(@types/node@20.19.11)(less@4.4.1) + specifier: ^6.0.5 + version: 6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0) + vite-plugin-checker: + specifier: ^0.8.0 + version: 0.8.0(eslint@9.34.0)(optionator@0.9.4)(typescript@5.9.2)(vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0)) - packages/shared: - dependencies: - antd-mobile: - specifier: ^5.34.0 - version: 5.39.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: - specifier: ^18.2.0 - version: 18.3.1 + packages/hooks: {} + + packages/shared: {} packages/utils: {} - projects/translate-h5: - dependencies: - '@icon-park/react': - specifier: ^1.4.2 - version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/node': - specifier: ^20.10.0 - version: 20.19.11 - '@types/react-router-dom': - specifier: ^5.3.3 - version: 5.3.3 - '@uidotdev/usehooks': - specifier: ^2.4.1 - version: 2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@vitejs/plugin-basic-ssl': - specifier: ^2.1.0 - version: 2.1.0(vite@5.4.19(@types/node@20.19.11)(less@4.4.1)) - '@workspace/shared': - specifier: workspace:* - version: link:../../packages/shared - '@workspace/utils': - specifier: workspace:* - version: link:../../packages/utils - antd-mobile: - specifier: ^5.33.0 - version: 5.39.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - antd-mobile-icons: - specifier: ^0.3.0 - version: 0.3.0 - axios: - specifier: ^1.6.2 - version: 1.11.0 - axios-hooks: - specifier: ^5.0.2 - version: 5.1.1(axios@1.11.0)(react@18.3.1) - js-audio-recorder: - specifier: ^1.0.7 - version: 1.0.7 - jsqr: - specifier: ^1.4.0 - version: 1.4.0 - less: - specifier: ^4.2.0 - version: 4.4.1 - query-string: - specifier: ^8.1.0 - version: 8.2.0 - react: - specifier: ^18.2.0 - version: 18.3.1 - react-audio-voice-recorder: - specifier: ^2.2.0 - version: 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) - react-router-dom: - specifier: ^6.20.0 - version: 6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-slick: - specifier: ^0.29.0 - version: 0.29.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - slick-carousel: - specifier: ^1.8.1 - version: 1.8.1(jquery@3.7.1) - weixin-js-sdk: - specifier: ^1.6.5 - version: 1.6.5 - zustand: - specifier: ^4.4.6 - version: 4.5.7(@types/react@18.3.24)(react@18.3.1) - devDependencies: - '@types/lodash.isequal': - specifier: ^4.5.8 - version: 4.5.8 - '@types/react': - specifier: ^18.3.24 - version: 18.3.24 - '@types/react-dom': - specifier: ^18.3.7 - version: 18.3.7(@types/react@18.3.24) - '@types/react-slick': - specifier: ^0.23.12 - version: 0.23.13 - '@typescript-eslint/eslint-plugin': - specifier: ^6.10.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2) - '@typescript-eslint/parser': - specifier: ^6.10.0 - version: 6.21.0(eslint@9.34.0)(typescript@5.9.2) - '@vitejs/plugin-react-swc': - specifier: ^3.5.0 - version: 3.11.0(vite@5.4.19(@types/node@20.19.11)(less@4.4.1)) - eslint: - specifier: ^9.34.0 - version: 9.34.0 - eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.34.0) - eslint-plugin-react-refresh: - specifier: ^0.4.20 - version: 0.4.20(eslint@9.34.0) - postcss-pxtorem: - specifier: ^6.0.0 - version: 6.1.0(postcss@8.5.6) - typescript: - specifier: ^5.2.2 - version: 5.9.2 - vite: - specifier: ^5.0.0 - version: 5.4.19(@types/node@20.19.11)(less@4.4.1) + projects/translate-h5: {} packages: + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.3': + resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.3': + resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.3': + resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.26.10': resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} engines: {node: '>=6.9.0'} @@ -245,146 +213,180 @@ packages: resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} engines: {node: '>=6.9.0'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.3': + resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.20.5': + resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.8.0': + resolution: {integrity: sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -393,6 +395,40 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-react/ast@1.53.0': + resolution: {integrity: sha512-TyJURQF4IEOLWUQvmCukc6GQsaqzW2ALwY97gy1AaCTR+9CXz12qF84JQydxVmph2LOndPJk1KCrnNkLAvxzIw==} + engines: {node: '>=18.18.0'} + + '@eslint-react/core@1.53.0': + resolution: {integrity: sha512-LP53OVMymLnEqJtmHeCyYMtFjK9Mw4F7lui5VO9YlbELopynPrpeiMPyVScxydLzW/toE7ZpDLTaB8B7Nrfdpw==} + engines: {node: '>=18.18.0'} + + '@eslint-react/eff@1.53.0': + resolution: {integrity: sha512-jlGTpgrLD+txK1ApUg7hX1/Oje2D9Bv/uHtnzdgBT6cI8vpt8EEXXclAxz9NN4insfEu+g5GZB8sQSvtsQCzwQ==} + engines: {node: '>=18.18.0'} + + '@eslint-react/eslint-plugin@1.53.0': + resolution: {integrity: sha512-/IOVFGKUdsDp2VxhecR+mywa0eKzBGi1K6x7+LL174rQN4bLhG9pUQctspGX3EJHH10Nw7Pdi8LYF9/LwL02XQ==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + '@eslint-react/kit@1.53.0': + resolution: {integrity: sha512-1OqcBpLtqsQSTgSBS8lxpoRojj7RLdZ0vBsnHlmWmARJhBV9+dlyu3scPduiohai3zjUdFLfLKvUqZpNeNbZig==} + engines: {node: '>=18.18.0'} + + '@eslint-react/shared@1.53.0': + resolution: {integrity: sha512-8cKjzAJZOmpwoH8KsbyAUpLd3N2Lw6N4TSVZ+W8OnsspOfLhmN9VEyuS442fiHwZ33+mXulVewfpKahFOb9XAA==} + engines: {node: '>=18.18.0'} + + '@eslint-react/var@1.53.0': + resolution: {integrity: sha512-9IKGvSUyVABV07A9IJDa3QMGvpXwlJzb6UegJMW5OxUA5fkKcAqPZPp7fgoLkhqCpN/8l/rwbhdq1PcHxgmFsQ==} + engines: {node: '>=18.18.0'} + '@eslint/config-array@0.21.0': resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -438,18 +474,14 @@ packages: resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -461,6 +493,22 @@ packages: react: '>=16.9' react-dom: '>=16.9' + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -477,6 +525,26 @@ packages: resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} engines: {node: '>=8.x'} + '@react-dev-inspector/babel-plugin@2.0.1': + resolution: {integrity: sha512-V2MzN9dj3uZu6NvAjSxXwa3+FOciVIuwAUwPLpO6ji5xpUyx8E6UiEng1QqzttdpacKHFKtkNYjtQAE+Lsqa5A==} + engines: {node: '>=12.0.0'} + + '@react-dev-inspector/middleware@2.0.1': + resolution: {integrity: sha512-qDMtBzAxNNAX01jjU1THZVuNiVB7J1Hjk42k8iLSSwfinc3hk667iqgdzeq1Za1a0V2bF5Ev6D4+nkZ+E1YUrQ==} + engines: {node: '>=12.0.0'} + + '@react-dev-inspector/umi3-plugin@2.0.1': + resolution: {integrity: sha512-lRw65yKQdI/1BwrRXWJEHDJel4DWboOartGmR3S5xiTF+EiOLjmndxdA5LoVSdqbcggdtq5SWcsoZqI0TkhH7Q==} + engines: {node: '>=12.0.0'} + + '@react-dev-inspector/umi4-plugin@2.0.1': + resolution: {integrity: sha512-vTefsJVAZsgpuO9IZ1ZFIoyryVUU+hjV8OPD8DfDU+po5LjVXc5Uncn+MkFOsT24AMpNdDvCnTRYiuSkFn8EsA==} + engines: {node: '>=12.0.0'} + + '@react-dev-inspector/vite-plugin@2.0.1': + resolution: {integrity: sha512-J1eI7cIm2IXE6EwhHR1OyoefvobUJEn/vJWEBwOM5uW4JkkLwuVoV9vk++XJyAmKUNQ87gdWZvSWrI2LjfrSug==} + engines: {node: '>=12.0.0'} + '@react-spring/animated@9.6.1': resolution: {integrity: sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==} peerDependencies: @@ -706,6 +774,12 @@ packages: '@swc/types@0.1.24': resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==} + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -718,14 +792,11 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/lodash.isequal@4.5.8': - resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + '@types/node@20.19.12': + resolution: {integrity: sha512-lSOjyS6vdO2G2g2CWrETTV3Jz2zlCXHpu1rcubLKpz9oj+z/1CceHlj+yq53W+9zgb98nSov/wjEKYDNauD+Hw==} - '@types/lodash@4.17.20': - resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} - - '@types/node@20.19.11': - resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -735,6 +806,11 @@ packages: peerDependencies: '@types/react': ^18.0.0 + '@types/react-reconciler@0.32.1': + resolution: {integrity: sha512-RsqPttsBQ+6af0nATFXJJpemYQH7kL9+xLNm1z+0MjQFDKBZDM2R6SBrjdvRmHu9i9fM6povACj57Ft+pKRNOA==} + peerDependencies: + '@types/react': '*' + '@types/react-router-dom@5.3.3': resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} @@ -747,66 +823,64 @@ packages: '@types/react@18.3.24': resolution: {integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==} - '@types/semver@7.7.0': - resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} - - '@typescript-eslint/eslint-plugin@6.21.0': - resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/eslint-plugin@8.42.0': + resolution: {integrity: sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.42.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@6.21.0': - resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/parser@8.42.0': + resolution: {integrity: sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@6.21.0': - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/type-utils@6.21.0': - resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/project-service@8.42.0': + resolution: {integrity: sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@6.21.0': - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/scope-manager@8.42.0': + resolution: {integrity: sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@6.21.0': - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/tsconfig-utils@8.42.0': + resolution: {integrity: sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@6.21.0': - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/type-utils@8.42.0': + resolution: {integrity: sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@6.21.0': - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/types@8.42.0': + resolution: {integrity: sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.42.0': + resolution: {integrity: sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.42.0': + resolution: {integrity: sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.42.0': + resolution: {integrity: sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@uidotdev/usehooks@2.4.1': resolution: {integrity: sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==} @@ -834,6 +908,63 @@ packages: peerDependencies: vite: ^4 || ^5 || ^6 || ^7 + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -844,6 +975,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + ahooks@3.9.5: resolution: {integrity: sha512-TrjXie49Q8HuHKTa84Fm9A+famMDAG1+7a9S9Gq6RQ0h90Jgqmiq3CkObuRjWT/C4d6nRZCw35Y2k2fmybb5eA==} engines: {node: '>=18'} @@ -851,9 +986,38 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -864,12 +1028,16 @@ packages: antd-mobile-v5-count@1.0.1: resolution: {integrity: sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA==} - antd-mobile@5.39.0: - resolution: {integrity: sha512-x0cr1KYcYEOzLzD8r5S3NYtViTxTkHSh8krjM5q6RxphjabvEFQTZuf3i7gJzICprirJ4GO/F7K3m8qldCiEjw==} + antd-mobile@5.40.0: + resolution: {integrity: sha512-nNfkTLiYPsa7A2i7eoG/hr2BM0agR4cfugE2+5HTyGnCg5xQT04Pmt9qEoKv7MOW5BaiiMyjO462Kh8KRF5QBA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -883,6 +1051,10 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + axios-hooks@5.1.1: resolution: {integrity: sha512-ti27vL2ttZUdOoBSwLzR63sW5zu0cC12jgOOw3PPhG6D8wajt4wfi9t9xUVpvK+6zHawcS2rHo9fKDTiOzOlgg==} peerDependencies: @@ -895,6 +1067,13 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + birecord@0.1.1: + resolution: {integrity: sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -905,6 +1084,14 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -913,10 +1100,21 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + caniuse-lite@1.0.30001739: + resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -931,12 +1129,29 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -947,6 +1162,14 @@ packages: dayjs@1.11.18: resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -967,6 +1190,10 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -975,6 +1202,11 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-port-alt@1.1.6: + resolution: {integrity: sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==} + engines: {node: '>= 4.2.1'} + hasBin: true + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -983,6 +1215,16 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + electron-to-chromium@1.5.214: + resolution: {integrity: sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + enquire.js@2.1.6: resolution: {integrity: sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==} @@ -990,6 +1232,9 @@ packages: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -998,6 +1243,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1006,26 +1254,97 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-plugin-react-debug@1.53.0: + resolution: {integrity: sha512-YLMYNFTmas1Q957qwzrbSybHueBNKTRCQMBD8zVE5+Grg2flhB+pOUxStRBQ3/1WlL93xIfe4mTJkb3HtI2wxA==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-dom@1.53.0: + resolution: {integrity: sha512-JfjDWxFLCuAo+Vm2S6uVGxHMteN37r193PORuCfERpi3LCh97xq0FI3j05SKHvyQmV87jUuBbKLvOBTylTSRvw==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-hooks-extra@1.53.0: + resolution: {integrity: sha512-9IkvlBl+92iKZ7+NnYxL54Js2LLaR5mSHDkbCFpm9SP+L8OgjshYzhJOzKog3WH3SfwaKgTy4BV0V8k5xJbF2g==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + eslint-plugin-react-hooks@5.2.0: resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react-naming-convention@1.53.0: + resolution: {integrity: sha512-te+Y/Z+WHtzQk7UUhl/tZrpZoS7ZkdRHpuxZRwmFrvWecNbWnJHrcrmNSHSkaYzb0kBqoJDrre3sHeJnWanKSQ==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + eslint-plugin-react-refresh@0.4.20: resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} peerDependencies: eslint: '>=8.40' + eslint-plugin-react-web-api@1.53.0: + resolution: {integrity: sha512-Cq6vQN0lpOoUsOv+7/5nzTDQA+dFFaRd16f0vlQ98wdlXHD+fj8Gfuy7IQlHWYNmPJInK6KaSs8gS074PoqrUg==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-react-x@1.53.0: + resolution: {integrity: sha512-5a1CpHtBXQQUPB6dbxvcqg97QJ+APWNbZJQEBKNCiVjnx1DpCOzhAwZ2jMOZe+HS96Cf3TqqlRf4HBUm4KYYxg==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + ts-api-utils: ^2.1.0 + typescript: ^4.9.5 || ^5.3.3 + peerDependenciesMeta: + ts-api-utils: + optional: true + typescript: + optional: true + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1060,6 +1379,10 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -1068,6 +1391,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1081,13 +1408,29 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filesize@8.0.7: + resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==} + engines: {node: '>= 0.4.0'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1096,6 +1439,10 @@ packages: resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} engines: {node: '>=14.16'} + find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1116,10 +1463,38 @@ packages: debug: optional: true + fork-ts-checker-webpack-plugin@6.5.3: + resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} + engines: {node: '>=10', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} + fs-extra@11.3.1: + resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} + engines: {node: '>=14.14'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1128,6 +1503,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1144,10 +1523,29 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -1162,6 +1560,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1178,6 +1580,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hotkeys-js@3.13.15: + resolution: {integrity: sha512-gHh8a/cPTCpanraePpjRxyIlxDFrIhYqjuh01UHWEwDpglJKCnvLW8kqSx5gQtOuSsJogNZXLhOdbSExpgUiqg==} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1186,11 +1591,18 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} hasBin: true + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1199,9 +1611,31 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + intersection-observer@0.12.2: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1210,19 +1644,37 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-immutable-type@5.0.1: + resolution: {integrity: sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg==} + peerDependencies: + eslint: '*' + typescript: '>=4.7.4' + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-root@2.1.0: + resolution: {integrity: sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==} + engines: {node: '>=6'} + is-url@1.2.4: resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + jquery@3.7.1: resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==} @@ -1240,24 +1692,51 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json2mq@0.2.0: resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsqr@1.4.0: resolution: {integrity: sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + less@4.4.1: resolution: {integrity: sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==} engines: {node: '>=14'} @@ -1267,6 +1746,21 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + engines: {node: '>= 12.13.0'} + + locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1284,10 +1778,13 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.1: + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} engines: {node: 20 || >=22} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -1296,6 +1793,13 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1320,10 +1824,13 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1343,6 +1850,9 @@ packages: engines: {node: '>= 4.4.x'} hasBin: true + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -1352,30 +1862,72 @@ packages: encoding: optional: true + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1384,6 +1936,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1391,10 +1946,18 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + postcss-pxtorem@6.1.0: resolution: {integrity: sha512-ROODSNci9ADal3zUcPHOF/K83TiCgNSPXQFSbwyPHNV8ioHIE4SaC+FPOufd8jsr5jV2uIz29v1Uqy1c4ov42g==} peerDependencies: @@ -1408,6 +1971,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -1425,6 +1992,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + rc-field-form@1.44.0: resolution: {integrity: sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA==} engines: {node: '>=8.x'} @@ -1462,11 +2032,30 @@ packages: react: '>=16.2.0' react-dom: '>=16.2.0' + react-dev-inspector@2.0.1: + resolution: {integrity: sha512-b8PAmbwGFrWcxeaX8wYveqO+VTwTXGJaz/yl9RO31LK1zeLKJVlkkbeLExLnJ6IvhXY1TwL8Q4+gR2GKJ8BI6Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + react: '>=16.8.0' + + react-dev-utils@12.0.1: + resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=2.7' + webpack: '>=4' + peerDependenciesMeta: + typescript: + optional: true + react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 + react-error-overlay@6.1.0: + resolution: {integrity: sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==} + react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} @@ -1496,12 +2085,24 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + recursive-readdir@2.2.3: + resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} + engines: {node: '>=6.0.0'} + regenerator-runtime@0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -1528,6 +2129,9 @@ packages: runes2@1.1.4: resolution: {integrity: sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1537,6 +2141,14 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + schema-utils@2.7.0: + resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} + engines: {node: '>= 8.9.0'} + + schema-utils@4.3.2: + resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + engines: {node: '>= 10.13.0'} + screenfull@5.2.0: resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} engines: {node: '>=0.10.0'} @@ -1545,11 +2157,18 @@ packages: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1558,6 +2177,13 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -1571,6 +2197,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -1587,6 +2216,13 @@ packages: string-convert@0.2.1: resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + string-ts@2.2.1: + resolution: {integrity: sha512-Q2u0gko67PLLhbte5HmPfdOjNvUKbKQM+mCNQae6jE91DmoFHY6HH9GcdqCeNx87DZ2KKjiFxmA0R/42OneGWw==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1595,6 +2231,53 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + tapable@1.1.3: + resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} + engines: {node: '>=6'} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} + engines: {node: '>=10'} + hasBin: true + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1602,11 +2285,19 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} + peerDependencies: + typescript: '>=4.0.0' + + ts-pattern@5.8.0: + resolution: {integrity: sha512-kIjN2qmWiHnhgr5DAkAafF9fwb0T5OhMVSWrm8XEdTFnX6+wfXwYOFjeF86UZ54vduqiR7BfqScFmXSzSaH8oA==} tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -1615,6 +2306,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} @@ -1623,6 +2318,16 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -1631,22 +2336,61 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite-plugin-checker@0.8.0: + resolution: {integrity: sha512-UA5uzOGm97UvZRTdZHiQVYFnd86AVn8EVaD4L3PoVzxH+IZSfaAw14WGFwX9QS23UW3lV/5bVKZn6l0w+q9P0g==} + engines: {node: '>=14.16'} + peerDependencies: + '@biomejs/biome': '>=1.7' + eslint: '>=7' + meow: ^9.0.0 + optionator: ^0.9.1 + stylelint: '>=13' + typescript: '*' + vite: '>=2.0.0' + vls: '*' + vti: '*' + vue-tsc: ~2.1.6 + peerDependenciesMeta: + '@biomejs/biome': + optional: true + eslint: + optional: true + meow: + optional: true + optionator: + optional: true + stylelint: + optional: true + typescript: + optional: true + vls: + optional: true + vti: + optional: true + vue-tsc: + optional: true + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -1661,16 +2405,66 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-jsonrpc@6.0.0: + resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} + engines: {node: '>=8.0.0 || >=10.0.0'} + + vscode-languageclient@7.0.0: + resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==} + engines: {vscode: ^1.52.0} + + vscode-languageserver-protocol@3.16.0: + resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.16.0: + resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} + + vscode-languageserver@7.0.0: + resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==} + hasBin: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + engines: {node: '>=10.13.0'} webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + + webpack@5.101.3: + resolution: {integrity: sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + weixin-js-sdk@1.6.5: resolution: {integrity: sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==} whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1680,10 +2474,23 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod@4.1.5: + resolution: {integrity: sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==} + zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} engines: {node: '>=12.7.0'} @@ -1701,88 +2508,300 @@ packages: snapshots: + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.3': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/helpers': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + + '@babel/parser@7.28.3': + dependencies: + '@babel/types': 7.28.2 + '@babel/runtime@7.26.10': dependencies: regenerator-runtime: 0.14.1 '@babel/runtime@7.28.3': {} - '@esbuild/aix-ppc64@0.21.5': + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.3': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.20.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + to-fast-properties: 2.0.0 + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@esbuild/aix-ppc64@0.25.9': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.25.9': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.25.9': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.25.9': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.25.9': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.25.9': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.25.9': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.25.9': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.9': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.25.9': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.25.9': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.25.9': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.25.9': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.25.9': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.25.9': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.25.9': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.25.9': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/openharmony-arm64@0.25.9': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/sunos-x64@0.25.9': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0)': + '@esbuild/win32-arm64@0.25.9': + optional: true + + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.25.9': + optional: true + + '@eslint-community/eslint-utils@4.8.0(eslint@9.34.0)': dependencies: eslint: 9.34.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} + '@eslint-react/ast@1.53.0(eslint@9.34.0)(typescript@5.9.2)': + dependencies: + '@eslint-react/eff': 1.53.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + string-ts: 2.2.1 + ts-pattern: 5.8.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/core@1.53.0(eslint@9.34.0)(typescript@5.9.2)': + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + birecord: 0.1.1 + ts-pattern: 5.8.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/eff@1.53.0': {} + + '@eslint-react/eslint-plugin@1.53.0(eslint@9.34.0)(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2)': + dependencies: + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 + eslint-plugin-react-debug: 1.53.0(eslint@9.34.0)(typescript@5.9.2) + eslint-plugin-react-dom: 1.53.0(eslint@9.34.0)(typescript@5.9.2) + eslint-plugin-react-hooks-extra: 1.53.0(eslint@9.34.0)(typescript@5.9.2) + eslint-plugin-react-naming-convention: 1.53.0(eslint@9.34.0)(typescript@5.9.2) + eslint-plugin-react-web-api: 1.53.0(eslint@9.34.0)(typescript@5.9.2) + eslint-plugin-react-x: 1.53.0(eslint@9.34.0)(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + - ts-api-utils + + '@eslint-react/kit@1.53.0(eslint@9.34.0)(typescript@5.9.2)': + dependencies: + '@eslint-react/eff': 1.53.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + ts-pattern: 5.8.0 + zod: 4.1.5 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/shared@1.53.0(eslint@9.34.0)(typescript@5.9.2)': + dependencies: + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + ts-pattern: 5.8.0 + zod: 4.1.5 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@eslint-react/var@1.53.0(eslint@9.34.0)(typescript@5.9.2)': + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + string-ts: 2.2.1 + ts-pattern: 5.8.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 @@ -1842,15 +2861,13 @@ snapshots: '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.3': {} '@icon-park/react@1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -1858,6 +2875,25 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1874,6 +2910,58 @@ snapshots: dependencies: '@babel/runtime': 7.28.3 + '@react-dev-inspector/babel-plugin@2.0.1': + dependencies: + '@babel/core': 7.28.3 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/traverse': 7.28.3 + '@babel/types': 7.20.5 + transitivePeerDependencies: + - supports-color + + '@react-dev-inspector/middleware@2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3)': + dependencies: + react-dev-utils: 12.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + + '@react-dev-inspector/umi3-plugin@2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3)': + dependencies: + '@react-dev-inspector/babel-plugin': 2.0.1 + '@react-dev-inspector/middleware': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + + '@react-dev-inspector/umi4-plugin@2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3)': + dependencies: + '@react-dev-inspector/babel-plugin': 2.0.1 + '@react-dev-inspector/middleware': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + + '@react-dev-inspector/vite-plugin@2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3)': + dependencies: + '@react-dev-inspector/middleware': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + '@react-spring/animated@9.6.1(react@18.3.1)': dependencies: '@react-spring/shared': 9.6.1(react@18.3.1) @@ -2026,6 +3114,16 @@ snapshots: dependencies: '@swc/counter': 0.1.3 + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@types/estree@1.0.8': {} '@types/history@4.7.11': {} @@ -2034,22 +3132,22 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/lodash.isequal@4.5.8': - dependencies: - '@types/lodash': 4.17.20 - - '@types/lodash@4.17.20': {} - - '@types/node@20.19.11': + '@types/node@20.19.12': dependencies: undici-types: 6.21.0 + '@types/parse-json@4.0.2': {} + '@types/prop-types@15.7.15': {} '@types/react-dom@18.3.7(@types/react@18.3.24)': dependencies: '@types/react': 18.3.24 + '@types/react-reconciler@0.32.1(@types/react@18.3.24)': + dependencies: + '@types/react': 18.3.24 + '@types/react-router-dom@5.3.3': dependencies: '@types/history': 4.7.11 @@ -2070,93 +3168,98 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.1.3 - '@types/semver@7.7.0': {} - - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 6.21.0(eslint@9.34.0)(typescript@5.9.2) - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.34.0)(typescript@5.9.2) - '@typescript-eslint/utils': 6.21.0(eslint@9.34.0)(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.4.1 + '@typescript-eslint/parser': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.42.0 eslint: 9.34.0 graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.5 natural-compare: 1.4.0 - semver: 7.7.2 - ts-api-utils: 1.4.3(typescript@5.9.2) - optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@9.34.0)(typescript@5.9.2)': + '@typescript-eslint/parser@8.42.0(eslint@9.34.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.42.0 debug: 4.4.1 eslint: 9.34.0 - optionalDependencies: typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@6.21.0': + '@typescript-eslint/project-service@8.42.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + debug: 4.4.1 + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color - '@typescript-eslint/type-utils@6.21.0(eslint@9.34.0)(typescript@5.9.2)': + '@typescript-eslint/scope-manager@8.42.0': dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.2) - '@typescript-eslint/utils': 6.21.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/visitor-keys': 8.42.0 + + '@typescript-eslint/tsconfig-utils@8.42.0(typescript@5.9.2)': + dependencies: + typescript: 5.9.2 + + '@typescript-eslint/type-utils@8.42.0(eslint@9.34.0)(typescript@5.9.2)': + dependencies: + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) debug: 4.4.1 eslint: 9.34.0 - ts-api-utils: 1.4.3(typescript@5.9.2) - optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@6.21.0': {} + '@typescript-eslint/types@8.42.0': {} - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.42.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/project-service': 8.42.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/visitor-keys': 8.42.0 debug: 4.4.1 - globby: 11.1.0 + fast-glob: 3.3.3 is-glob: 4.0.3 - minimatch: 9.0.3 + minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 1.4.3(typescript@5.9.2) - optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@6.21.0(eslint@9.34.0)(typescript@5.9.2)': + '@typescript-eslint/utils@8.42.0(eslint@9.34.0)(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.7.0 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.2) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.34.0) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) eslint: 9.34.0 - semver: 7.7.2 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@6.21.0': + '@typescript-eslint/visitor-keys@8.42.0': dependencies: - '@typescript-eslint/types': 6.21.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.42.0 + eslint-visitor-keys: 4.2.1 '@uidotdev/usehooks@2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -2170,24 +3273,110 @@ snapshots: '@use-gesture/core': 10.3.0 react: 18.3.1 - '@vitejs/plugin-basic-ssl@2.1.0(vite@5.4.19(@types/node@20.19.11)(less@4.4.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0))': dependencies: - vite: 5.4.19(@types/node@20.19.11)(less@4.4.1) + vite: 6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0) - '@vitejs/plugin-react-swc@3.11.0(vite@5.4.19(@types/node@20.19.11)(less@4.4.1))': + '@vitejs/plugin-react-swc@3.11.0(vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.27 '@swc/core': 1.13.5 - vite: 5.4.19(@types/node@20.19.11)(less@4.4.1) + vite: 6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0) transitivePeerDependencies: - '@swc/helpers' + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn-import-phases@1.0.4(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + address@1.2.2: {} + ahooks@3.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.28.3 @@ -2203,6 +3392,19 @@ snapshots: screenfull: 5.2.0 tslib: 2.8.1 + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -2210,6 +3412,19 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -2218,7 +3433,7 @@ snapshots: antd-mobile-v5-count@1.0.1: {} - antd-mobile@5.39.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + antd-mobile@5.40.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@floating-ui/dom': 1.7.4 '@rc-component/mini-decimal': 1.1.0 @@ -2243,6 +3458,11 @@ snapshots: tslib: 2.8.1 use-sync-external-store: 1.5.0(react@18.3.1) + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + argparse@2.0.1: {} array-union@2.1.0: {} @@ -2251,12 +3471,14 @@ snapshots: asynckit@0.4.0: {} + at-least-node@1.0.0: {} + axios-hooks@5.1.1(axios@1.11.0)(react@18.3.1): dependencies: '@babel/runtime': 7.26.10 axios: 1.11.0 dequal: 2.0.3 - lru-cache: 11.1.0 + lru-cache: 11.2.1 react: 18.3.1 axios@1.11.0: @@ -2269,6 +3491,10 @@ snapshots: balanced-match@1.0.2: {} + binary-extensions@2.3.0: {} + + birecord@0.1.1: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -2282,6 +3508,15 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.25.4: + dependencies: + caniuse-lite: 1.0.30001739 + electron-to-chromium: 1.5.214 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.4) + + buffer-from@1.1.2: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -2289,11 +3524,27 @@ snapshots: callsites@3.1.0: {} + caniuse-lite@1.0.30001739: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chrome-trace-event@1.0.4: {} + classnames@2.5.1: {} color-convert@2.0.1: @@ -2306,12 +3557,28 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@2.20.3: {} + + commander@8.3.0: {} + + compare-versions@6.1.1: {} + concat-map@0.0.1: {} + convert-source-map@2.0.0: {} + copy-anything@2.0.6: dependencies: is-what: 3.14.1 + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2322,6 +3589,10 @@ snapshots: dayjs@1.11.18: {} + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@4.4.1: dependencies: ms: 2.1.3 @@ -2332,10 +3603,19 @@ snapshots: deepmerge@4.3.1: {} + define-lazy-prop@2.0.0: {} + delayed-stream@1.0.0: {} dequal@2.0.3: {} + detect-port-alt@1.1.6: + dependencies: + address: 1.2.2 + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -2346,6 +3626,15 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer@0.1.2: {} + + electron-to-chromium@1.5.214: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.3 + enquire.js@2.1.6: {} errno@0.1.8: @@ -2353,10 +3642,16 @@ snapshots: prr: 1.0.1 optional: true + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -2368,42 +3663,174 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - esbuild@0.21.5: + esbuild@0.25.9: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} + eslint-plugin-react-debug@1.53.0(eslint@9.34.0)(typescript@5.9.2): + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/core': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 + string-ts: 2.2.1 + ts-pattern: 5.8.0 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-dom@1.53.0(eslint@9.34.0)(typescript@5.9.2): + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/core': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + compare-versions: 6.1.1 + eslint: 9.34.0 + string-ts: 2.2.1 + ts-pattern: 5.8.0 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-hooks-extra@1.53.0(eslint@9.34.0)(typescript@5.9.2): + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/core': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 + string-ts: 2.2.1 + ts-pattern: 5.8.0 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + eslint-plugin-react-hooks@5.2.0(eslint@9.34.0): dependencies: eslint: 9.34.0 + eslint-plugin-react-naming-convention@1.53.0(eslint@9.34.0)(typescript@5.9.2): + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/core': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 + string-ts: 2.2.1 + ts-pattern: 5.8.0 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + eslint-plugin-react-refresh@0.4.20(eslint@9.34.0): dependencies: eslint: 9.34.0 + eslint-plugin-react-web-api@1.53.0(eslint@9.34.0)(typescript@5.9.2): + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/core': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 + string-ts: 2.2.1 + ts-pattern: 5.8.0 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-x@1.53.0(eslint@9.34.0)(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2): + dependencies: + '@eslint-react/ast': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/core': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/eff': 1.53.0 + '@eslint-react/kit': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/shared': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@eslint-react/var': 1.53.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.42.0 + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + compare-versions: 6.1.1 + eslint: 9.34.0 + is-immutable-type: 5.0.1(eslint@9.34.0)(typescript@5.9.2) + string-ts: 2.2.1 + ts-pattern: 5.8.0 + optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -2415,7 +3842,7 @@ snapshots: eslint@9.34.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.34.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.1 @@ -2423,7 +3850,7 @@ snapshots: '@eslint/eslintrc': 3.3.1 '@eslint/js': 9.34.0 '@eslint/plugin-kit': 0.3.5 - '@humanfs/node': 0.16.6 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 @@ -2467,10 +3894,14 @@ snapshots: dependencies: estraverse: 5.3.0 + estraverse@4.3.0: {} + estraverse@5.3.0: {} esutils@2.0.3: {} + events@3.3.0: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -2485,20 +3916,32 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 + filesize@8.0.7: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 filter-obj@5.1.0: {} + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2513,6 +3956,26 @@ snapshots: follow-redirects@1.15.11: {} + fork-ts-checker-webpack-plugin@6.5.3(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3): + dependencies: + '@babel/code-frame': 7.27.1 + '@types/json-schema': 7.0.15 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 6.0.0 + deepmerge: 4.3.1 + fs-extra: 9.1.0 + glob: 7.2.3 + memfs: 3.5.3 + minimatch: 3.1.2 + schema-utils: 2.7.0 + semver: 7.7.2 + tapable: 1.1.3 + typescript: 5.9.2 + webpack: 5.101.3 + optionalDependencies: + eslint: 9.34.0 + form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -2521,11 +3984,30 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + fs-extra@11.3.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-monkey@1.1.0: {} + + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2552,8 +4034,31 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + globals@14.0.0: {} + globals@15.15.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -2565,11 +4070,14 @@ snapshots: gopd@1.2.0: {} - graceful-fs@4.2.11: - optional: true + graceful-fs@4.2.11: {} graphemer@1.4.0: {} + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -2582,6 +4090,8 @@ snapshots: dependencies: function-bind: 1.1.2 + hotkeys-js@3.13.15: {} + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -2589,9 +4099,13 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.5: {} + image-size@0.5.5: optional: true + immer@9.0.21: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2599,22 +4113,61 @@ snapshots: imurmurhash@0.1.4: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + intersection-observer@0.12.2: {} + is-arrayish@0.2.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-docker@2.2.1: {} + is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + is-immutable-type@5.0.1(eslint@9.34.0)(typescript@5.9.2): + dependencies: + '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + ts-declaration-location: 1.0.7(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + is-number@7.0.0: {} + is-root@2.1.0: {} + is-url@1.2.4: {} is-what@3.14.1: {} + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + isexe@2.0.0: {} + jest-worker@27.5.1: + dependencies: + '@types/node': 20.19.12 + merge-stream: 2.0.0 + supports-color: 8.1.1 + jquery@3.7.1: {} js-audio-recorder@1.0.7: {} @@ -2627,22 +4180,40 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json2mq@0.2.0: dependencies: string-convert: 0.2.1 + json5@2.2.3: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + jsqr@1.4.0: {} keyv@4.5.4: dependencies: json-buffer: 3.0.1 + kind-of@6.0.3: {} + + kleur@3.0.3: {} + less@4.4.1: dependencies: copy-anything: 2.0.6 @@ -2662,6 +4233,17 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + + loader-runner@4.3.0: {} + + loader-utils@3.3.1: {} + + locate-path@3.0.0: + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -2676,7 +4258,11 @@ snapshots: dependencies: js-tokens: 4.0.0 - lru-cache@11.1.0: {} + lru-cache@11.2.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 make-dir@2.1.0: dependencies: @@ -2686,6 +4272,12 @@ snapshots: math-intrinsics@1.1.0: {} + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + merge-stream@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -2706,10 +4298,12 @@ snapshots: dependencies: brace-expansion: 1.1.12 - minimatch@9.0.3: + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 + ms@2.0.0: {} + ms@2.1.3: {} nano-memoize@3.0.16: {} @@ -2724,10 +4318,30 @@ snapshots: sax: 1.4.1 optional: true + neo-async@2.6.2: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + node-releases@2.0.19: {} + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -2737,33 +4351,62 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-locate@3.0.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 + p-try@2.2.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse-node-version@1.0.1: {} + path-exists@3.0.0: {} + path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-type@4.0.0: {} + picocolors@1.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.3: {} + pify@4.0.1: optional: true + pkg-up@3.1.0: + dependencies: + find-up: 3.0.0 + postcss-pxtorem@6.1.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -2776,6 +4419,11 @@ snapshots: prelude-ls@1.2.1: {} + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + proxy-from-env@1.1.0: {} prr@1.0.1: @@ -2791,6 +4439,10 @@ snapshots: queue-microtask@1.2.3: {} + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + rc-field-form@1.44.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.28.3 @@ -2837,12 +4489,68 @@ snapshots: transitivePeerDependencies: - encoding + react-dev-inspector@2.0.1(@types/react@18.3.24)(eslint@9.34.0)(react@18.3.1)(typescript@5.9.2)(webpack@5.101.3): + dependencies: + '@react-dev-inspector/babel-plugin': 2.0.1 + '@react-dev-inspector/middleware': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + '@react-dev-inspector/umi3-plugin': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + '@react-dev-inspector/umi4-plugin': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + '@react-dev-inspector/vite-plugin': 2.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + '@types/react-reconciler': 0.32.1(@types/react@18.3.24) + hotkeys-js: 3.13.15 + picocolors: 1.0.0 + react: 18.3.1 + react-dev-utils: 12.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + transitivePeerDependencies: + - '@types/react' + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + + react-dev-utils@12.0.1(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3): + dependencies: + '@babel/code-frame': 7.27.1 + address: 1.2.2 + browserslist: 4.25.4 + chalk: 4.1.2 + cross-spawn: 7.0.6 + detect-port-alt: 1.1.6 + escape-string-regexp: 4.0.0 + filesize: 8.0.7 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.34.0)(typescript@5.9.2)(webpack@5.101.3) + global-modules: 2.0.0 + globby: 11.1.0 + gzip-size: 6.0.0 + immer: 9.0.21 + is-root: 2.1.0 + loader-utils: 3.3.1 + open: 8.4.2 + pkg-up: 3.1.0 + prompts: 2.4.2 + react-error-overlay: 6.1.0 + recursive-readdir: 2.2.3 + shell-quote: 1.8.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + webpack: 5.101.3 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 + react-error-overlay@6.1.0: {} + react-fast-compare@3.2.2: {} react-is@18.3.1: {} @@ -2873,10 +4581,20 @@ snapshots: dependencies: loose-envify: 1.4.0 + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + recursive-readdir@2.2.3: + dependencies: + minimatch: 3.1.2 + regenerator-runtime@0.13.11: {} regenerator-runtime@0.14.1: {} + require-from-string@2.0.2: {} + resize-observer-polyfill@1.5.1: {} resolve-from@4.0.0: {} @@ -2918,6 +4636,8 @@ snapshots: runes2@1.1.4: {} + safe-buffer@5.2.1: {} + safer-buffer@2.1.2: optional: true @@ -2928,19 +4648,42 @@ snapshots: dependencies: loose-envify: 1.4.0 + schema-utils@2.7.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@4.3.2: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + screenfull@5.2.0: {} semver@5.7.2: optional: true + semver@6.3.1: {} + semver@7.7.2: {} + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} + shell-quote@1.8.3: {} + + sisteransi@1.0.5: {} + slash@3.0.0: {} slick-carousel@1.8.1(jquery@3.7.1): @@ -2949,8 +4692,12 @@ snapshots: source-map-js@1.2.1: {} - source-map@0.6.1: - optional: true + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} split-on-first@3.0.0: {} @@ -2960,32 +4707,90 @@ snapshots: string-convert@0.2.1: {} + string-ts@2.2.1: {} + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-json-comments@3.1.1: {} supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + tapable@1.1.3: {} + + tapable@2.2.3: {} + + terser-webpack-plugin@5.3.14(webpack@5.101.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.44.0 + webpack: 5.101.3 + + terser@5.44.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-table@0.2.0: {} + + tiny-invariant@1.3.3: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-fast-properties@2.0.0: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 tr46@0.0.3: {} - ts-api-utils@1.4.3(typescript@5.9.2): + ts-api-utils@2.1.0(typescript@5.9.2): dependencies: typescript: 5.9.2 + ts-declaration-location@1.0.7(typescript@5.9.2): + dependencies: + picomatch: 4.0.3 + typescript: 5.9.2 + + ts-pattern@5.8.0: {} + tslib@2.8.1: {} type-check@0.4.0: dependencies: prelude-ls: 1.2.1 + type-fest@0.21.3: {} + typescript@5.9.2: {} undici-types@6.21.0: {} + universalify@2.0.1: {} + + update-browserslist-db@1.1.3(browserslist@4.25.4): + dependencies: + browserslist: 4.25.4 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -2994,18 +4799,106 @@ snapshots: dependencies: react: 18.3.1 - vite@5.4.19(@types/node@20.19.11)(less@4.4.1): + vite-plugin-checker@0.8.0(eslint@9.34.0)(optionator@0.9.4)(typescript@5.9.2)(vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0)): dependencies: - esbuild: 0.21.5 + '@babel/code-frame': 7.27.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + chokidar: 3.6.0 + commander: 8.3.0 + fast-glob: 3.3.3 + fs-extra: 11.3.1 + npm-run-path: 4.0.1 + strip-ansi: 6.0.1 + tiny-invariant: 1.3.3 + vite: 6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0) + vscode-languageclient: 7.0.0 + vscode-languageserver: 7.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + eslint: 9.34.0 + optionator: 0.9.4 + typescript: 5.9.2 + + vite@6.3.5(@types/node@20.19.12)(less@4.4.1)(terser@5.44.0): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.50.0 + tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 20.19.11 + '@types/node': 20.19.12 fsevents: 2.3.3 less: 4.4.1 + terser: 5.44.0 + + vscode-jsonrpc@6.0.0: {} + + vscode-languageclient@7.0.0: + dependencies: + minimatch: 3.1.2 + semver: 7.7.2 + vscode-languageserver-protocol: 3.16.0 + + vscode-languageserver-protocol@3.16.0: + dependencies: + vscode-jsonrpc: 6.0.0 + vscode-languageserver-types: 3.16.0 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.16.0: {} + + vscode-languageserver@7.0.0: + dependencies: + vscode-languageserver-protocol: 3.16.0 + + vscode-uri@3.1.0: {} + + watchpack@2.4.4: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 webidl-conversions@3.0.1: {} + webpack-sources@3.3.3: {} + + webpack@5.101.3: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.4 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.3 + terser-webpack-plugin: 5.3.14(webpack@5.101.3) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + weixin-js-sdk@1.6.5: {} whatwg-url@5.0.0: @@ -3013,17 +4906,30 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 word-wrap@1.2.5: {} + wrappy@1.0.2: {} + + yallist@3.1.1: {} + + yaml@1.10.2: {} + yocto-queue@0.1.0: {} - zustand@4.5.7(@types/react@18.3.24)(react@18.3.1): + zod@4.1.5: {} + + zustand@4.5.7(@types/react@18.3.24)(immer@9.0.21)(react@18.3.1): dependencies: use-sync-external-store: 1.5.0(react@18.3.1) optionalDependencies: '@types/react': 18.3.24 + immer: 9.0.21 react: 18.3.1 diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index fa5e842..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,13 +0,0 @@ -export default { - plugins: { - 'postcss-pxtorem': { - rootValue: 16, - unitPrecision: 5, - propList: ['*'], - selectorBlackList: [], - replace: true, - mediaQuery: false, - minPixelValue: 0, - }, - }, -}; diff --git a/projects/translate-h5/package.json b/projects/translate-h5/package.json index a6d37ad..f4d9164 100644 --- a/projects/translate-h5/package.json +++ b/projects/translate-h5/package.json @@ -8,45 +8,5 @@ "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" - }, - "dependencies": { - "@icon-park/react": "^1.4.2", - "@types/node": "^20.10.0", - "@types/react-router-dom": "^5.3.3", - "@uidotdev/usehooks": "^2.4.1", - "@vitejs/plugin-basic-ssl": "^2.1.0", - "antd-mobile": "^5.33.0", - "antd-mobile-icons": "^0.3.0", - "axios": "^1.6.2", - "axios-hooks": "^5.0.2", - "js-audio-recorder": "^1.0.7", - "jsqr": "^1.4.0", - "less": "^4.2.0", - "query-string": "^8.1.0", - "react": "^18.2.0", - "react-audio-voice-recorder": "^2.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.20.0", - "react-slick": "^0.29.0", - "slick-carousel": "^1.8.1", - "weixin-js-sdk": "^1.6.5", - "zustand": "^4.4.6", - "@workspace/shared": "workspace:*", - "@workspace/utils": "workspace:*" - }, - "devDependencies": { - "@types/lodash.isequal": "^4.5.8", - "@types/react": "^18.3.24", - "@types/react-dom": "^18.3.7", - "@types/react-slick": "^0.23.12", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^9.34.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "postcss-pxtorem": "^6.0.0", - "typescript": "^5.2.2", - "vite": "^5.0.0" } } diff --git a/projects/translate-h5/postcss.config.js b/projects/translate-h5/postcss.config.js new file mode 100644 index 0000000..d15b563 --- /dev/null +++ b/projects/translate-h5/postcss.config.js @@ -0,0 +1,14 @@ +export default { + plugins: { + "postcss-pxtorem": { + rootValue: 16, // 75表示750设计稿,37.5表示375设计稿(建议设置成37.5,因为可以兼容大部分移动端ui库) + unitPrecision: 5, + propList: ["*"], + selectorBlackList: [], + replace: true, + mediaQuery: false, + minPixelValue: 0, + selectorBlackList: ["norem"], // 过滤掉norem-开头的class,不进行rem转换 + }, + }, +}; diff --git a/projects/translate-h5/src/hooks/i18n.ts b/projects/translate-h5/src/hooks/i18n.ts new file mode 100644 index 0000000..9b32a34 --- /dev/null +++ b/projects/translate-h5/src/hooks/i18n.ts @@ -0,0 +1,12 @@ +import zhCN from "@/locales/zh_CN.json"; +import enUS from "@/locales/en_US.json"; +import { useI18nStore } from "@/store/i18n"; + +export default function useI18n() { + const { lang } = useI18nStore(); + const locales = lang === "en_US" ? enUS : zhCN; + + return (name: string) => { + return locales[name as keyof typeof locales] || name; + }; +} diff --git a/projects/translate-h5/src/hooks/location.ts b/projects/translate-h5/src/hooks/location.ts new file mode 100644 index 0000000..2b1b1e2 --- /dev/null +++ b/projects/translate-h5/src/hooks/location.ts @@ -0,0 +1,63 @@ +import { useState, useEffect } from "react"; +import useI18n from "./i18n"; + +// 定义位置坐标的类型 +export interface Coordinates { + lat: number; + lng: number; +} + +// 定义返回的位置状态的类型 +export interface LocationState { + loaded: boolean; + coordinates: Coordinates | null; +} + +// 定义错误状态的类型 +export type ErrorState = string | null; + +export const useLocation = (): { + location: LocationState; + error: ErrorState; +} => { + const t = useI18n(); + + // 用于存储位置信息和加载状态的状态 + const [location, setLocation] = useState({ + loaded: false, + coordinates: null, + }); + + // 用于存储任何错误消息的状态 + const [error, setError] = useState(null); + + // 地理位置成功处理函数 + const onSuccess = (location: GeolocationPosition) => { + setLocation({ + loaded: true, + coordinates: { + lat: location.coords.latitude, + lng: location.coords.longitude, + }, + }); + }; + + // 地理位置错误处理函数 + const onError = (error: GeolocationPositionError) => { + setError(error.message); + }; + + // 使用 useEffect 在组件挂载时执行地理位置请求 + useEffect(() => { + // 检查浏览器是否支持地理位置 + if (!navigator.geolocation) { + setError(t("hooks.location.unsupported")); + return; + } + + // 请求用户的当前位置 + navigator.geolocation.getCurrentPosition(onSuccess, onError); + }, []); + + return { location, error }; +}; diff --git a/packages/hooks/src/session.ts b/projects/translate-h5/src/hooks/session.ts similarity index 100% rename from packages/hooks/src/session.ts rename to projects/translate-h5/src/hooks/session.ts diff --git a/packages/hooks/src/useFileUpload.ts b/projects/translate-h5/src/hooks/useFileUpload.ts similarity index 100% rename from packages/hooks/src/useFileUpload.ts rename to projects/translate-h5/src/hooks/useFileUpload.ts diff --git a/projects/translate-h5/src/layout/main/mainLayout.tsx b/projects/translate-h5/src/layout/main/mainLayout.tsx index 51282c5..8c042c1 100644 --- a/projects/translate-h5/src/layout/main/mainLayout.tsx +++ b/projects/translate-h5/src/layout/main/mainLayout.tsx @@ -11,12 +11,7 @@ interface MainLayoutProps { onLink?: () => void; } -const MainLayout: React.FC = ({ - isShowNavBar, - children, - onLink, - title, -}) => { +const MainLayout: React.FC = ({ isShowNavBar, children, onLink, title }) => { const navigate = useNavigate(); const location = useLocation(); const { pathname } = location; diff --git a/projects/translate-h5/src/route/auth.tsx b/projects/translate-h5/src/route/auth.tsx index 1406e73..78fc1c9 100644 --- a/projects/translate-h5/src/route/auth.tsx +++ b/projects/translate-h5/src/route/auth.tsx @@ -1,9 +1,14 @@ -import React, {useEffect} from 'react'; -import {useNavigate} from 'react-router-dom'; +import React, { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { AppRoute } from "./routes"; interface AuthRouteProps { - children: React.ReactNode; - auth?: boolean; + children: React.ReactNode; + auth?: boolean; + path: string; + meta?: { + title: string; + }; } /** @@ -12,18 +17,24 @@ interface AuthRouteProps { * @param auth 是否需要认证 * @constructor 认证路由组件 */ -const AuthRoute: React.FC = ({children, auth}) => { - const navigate = useNavigate(); - const token = localStorage.getItem('token'); // 或者其他认证令牌的获取方式 - const isAuthenticated = Boolean(token); // 认证逻辑 +const AuthRoute: React.FC = ({ children, auth, meta }) => { + const navigate = useNavigate(); + const token = localStorage.getItem("token"); // 或者其他认证令牌的获取方式 + const isAuthenticated = Boolean(token); // 认证逻辑 + console.log(auth); + useEffect(() => { + if (meta?.title) { + document.title = meta.title; + } + }, [meta]); + useEffect(() => { + // 检查角色权限 + if (auth && !isAuthenticated) { + navigate("/login"); // 如果未认证且路由需要认证,则重定向到登录 + } + }, [auth, isAuthenticated, navigate]); - useEffect(() => { - if (auth && !isAuthenticated) { - navigate('/login'); // 如果未认证且路由需要认证,则重定向到登录 - } - }, [auth, isAuthenticated, navigate]); - - return <>{children}; + return <>{children}; }; export default AuthRoute; diff --git a/projects/translate-h5/src/route/render-routes.tsx b/projects/translate-h5/src/route/render-routes.tsx index 12d6537..d0071a5 100644 --- a/projects/translate-h5/src/route/render-routes.tsx +++ b/projects/translate-h5/src/route/render-routes.tsx @@ -1,27 +1,48 @@ -import {Route, Routes} from 'react-router-dom'; -import {routes, AppRoute} from './routes'; -import AuthRoute from './auth.tsx'; +import { Route, Routes } from "react-router-dom"; +import { routes, AppRoute } from "./routes"; +import AuthRoute from "./auth.tsx"; +import { Suspense } from "react"; +import { DotLoading } from "antd-mobile"; /** * 渲染路由 * @constructor RenderRoutes */ export const RenderRoutes = () => { - const renderRoutes = (routes: AppRoute[]) => { - return routes.map(route => ( - - {route.element} - - } - > - {route.children && renderRoutes(route.children)} - - )); - }; + const renderRoutes = (routes: AppRoute[]) => { + return routes.map((route) => ( + + {route.element} + + } + > + {route.children && renderRoutes(route.children)} + + )); + }; - return {renderRoutes(routes)}; + return ( + + +
+ } + > + {renderRoutes(routes)} + + ); }; diff --git a/projects/translate-h5/src/route/routes.tsx b/projects/translate-h5/src/route/routes.tsx index 44b59ee..164e56b 100644 --- a/projects/translate-h5/src/route/routes.tsx +++ b/projects/translate-h5/src/route/routes.tsx @@ -1,19 +1,35 @@ -import React from "react"; -import Home from "@/view/home"; -import TranslateDetail from "@/view/home/detail"; -import Setting from "@/view/setting"; -import Page404 from "@/view/error/page404"; +import { Navigate } from "react-router-dom"; +import { lazy } from "react"; + export interface AppRoute { path: string; element: React.ReactNode; auth?: boolean; children?: AppRoute[]; + meta?: { + title: string; + }; } - +const Home = lazy(() => import("@/view/home")); +const Page404 = lazy(() => import("@/view/error/page404")); +const TranslateDetail = lazy(() => import("@/view/home/detail")); export const routes: AppRoute[] = [ - { path: "/", element: , auth: false }, - { path: "/set", element: , auth: false }, - { path: "/detail", element: , auth: false }, - { path: "/mood", element: , auth: false }, + { + path: "/", + element: , + auth: false, + meta: { + title: "宠物翻译", + }, + }, + { + path: "/translate", + element: , + auth: false, + meta: { + title: "宠物翻译", + }, + }, + { path: "/translate/detail", element: , auth: false }, { path: "*", element: , auth: false }, ]; diff --git a/projects/translate-h5/src/types/global.d.ts b/projects/translate-h5/src/types/global.d.ts new file mode 100644 index 0000000..0aaab19 --- /dev/null +++ b/projects/translate-h5/src/types/global.d.ts @@ -0,0 +1,19 @@ +// src/types/global.d.ts +declare module "*.module.less" { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module "*.module.css" { + const classes: { readonly [key: string]: string }; + export default classes; +} + +// 其他资源文件 +declare module "*.png"; +declare module "*.jpg"; +declare module "*.jpeg"; +declare module "*.gif"; +declare module "*.svg"; +declare module "*.ico"; +declare module "*.webp"; diff --git a/projects/translate-h5/src/view/home/component/search/index.tsx b/projects/translate-h5/src/view/home/component/search/index.tsx index d371724..422c48f 100644 --- a/projects/translate-h5/src/view/home/component/search/index.tsx +++ b/projects/translate-h5/src/view/home/component/search/index.tsx @@ -15,7 +15,7 @@ interface PropsConfig { handleAllAni: () => void; } const allAni = ["全部宠物", "丑丑", "胖胖", "可可"]; -function Index(props: PropsConfig) { +function SearchCom(props: PropsConfig) { const [aniName, setAniName] = useState("全部宠物"); const animenuRef = useRef(null); const handleAniSelect = (val: RadioValue) => { @@ -84,4 +84,4 @@ function Index(props: PropsConfig) { ); } -export default Index; +export default SearchCom; diff --git a/projects/translate-h5/src/view/home/index.less b/projects/translate-h5/src/view/home/index.less index e32b8cc..35ac51a 100644 --- a/projects/translate-h5/src/view/home/index.less +++ b/projects/translate-h5/src/view/home/index.less @@ -1,39 +1,41 @@ .home { height: 100%; overflow: hidden; - .adm-tabs { - display: flex; - flex-direction: column; - height: 100%; - } - .adm-tabs-content { - flex: 1; - overflow: hidden; - padding: 0px; - } - .adm-tabs-header { - border: 0 none; - position: sticky; - top: 0px; - background: #fff; - z-index: 99; - } + // .adm-tabs { + // display: flex; + // flex-direction: column; + // height: 100%; + // } + // .adm-tabs-content { + // flex: 1; + // overflow: hidden; + // padding: 0px; + // } + // .adm-tabs-header { + // border: 0 none; + // position: sticky; + // top: 0px; + // background: #fff; + // z-index: 99; + // } - .adm-tabs-tab { - font-size: 20px; - color: rgba(0, 0, 0, 0.25); - font-weight: 600; - &.adm-tabs-tab-active { - color: #000; - } - } - .adm-tabs-tab-line { - height: 0px; - } + // .adm-tabs-tab { + // font-size: 20px; + // color: rgba(0, 0, 0, 0.25); + // font-weight: 600; + // &.adm-tabs-tab-active { + // color: #000; + // } + // } + // .adm-tabs-tab-line { + // height: 0px; + // } .translate-container { display: flex; flex-direction: column; height: 100%; + .header { + } } } diff --git a/projects/translate-h5/src/view/home/index.tsx b/projects/translate-h5/src/view/home/index.tsx index 41f03b8..cf93933 100644 --- a/projects/translate-h5/src/view/home/index.tsx +++ b/projects/translate-h5/src/view/home/index.tsx @@ -15,14 +15,18 @@ function Index() { return (
- +
+

宠物翻译

+
+
+ {/* 2 - +
*/}
); diff --git a/projects/translate-h5/src/view/home/translate.tsx b/projects/translate-h5/src/view/home/translate.tsx index 0b9701e..3c5d328 100644 --- a/projects/translate-h5/src/view/home/translate.tsx +++ b/projects/translate-h5/src/view/home/translate.tsx @@ -2,11 +2,7 @@ import { useCallback, useEffect, useState } from "react"; import { Image, Toast } from "antd-mobile"; import MessageCom from "./component/message"; import VoiceRecord from "./component/voice"; -import { - XPopup, - FloatingMenu, - type FloatMenuItemConfig, -} from "@workspace/shared"; +import { XPopup, FloatingMenu, type FloatMenuItemConfig } from "@workspace/shared"; import type { Message } from "./types"; import { mockTranslateAudio } from "@/utils/voice"; @@ -14,21 +10,14 @@ import dogSvg from "@/assets/translate/dog.svg"; import catSvg from "@/assets/translate/cat.svg"; import pigSvg from "@/assets/translate/pig.svg"; import { MoreTwo } from "@icon-park/react"; +import SearchCom from "./component/search"; interface DefinedProps {} const menuItems: FloatMenuItemConfig[] = [ { icon: , type: "dog" }, { icon: , type: "cat" }, { icon: , type: "pig" }, { - icon: ( - - ), + icon: , type: "add", }, ]; @@ -85,9 +74,7 @@ function Index(props: DefinedProps) { setMessages((prev) => prev.map((msg) => - msg.id === messageId - ? { ...msg, translatedText, isTranslating: false } - : msg + msg.id === messageId ? { ...msg, translatedText, isTranslating: false } : msg ) ); } catch (error) { @@ -124,17 +111,17 @@ function Index(props: DefinedProps) { return (
+
+ {}} /> +
+ - + qq; -} - -export default Index; diff --git a/projects/translate-h5/tsconfig.json b/projects/translate-h5/tsconfig.json index f32e379..87741a3 100644 --- a/projects/translate-h5/tsconfig.json +++ b/projects/translate-h5/tsconfig.json @@ -1,5 +1,4 @@ { - // "extends": "../../tsconfig.json", "compilerOptions": { "composite": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", diff --git a/projects/translate-h5/vite.config.ts b/projects/translate-h5/vite.config.ts index 9be4cc1..fd55193 100644 --- a/projects/translate-h5/vite.config.ts +++ b/projects/translate-h5/vite.config.ts @@ -1,9 +1,9 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; -import path from "path"; +import path, { resolve } from "path"; +import { inspectorServer } from "@react-dev-inspector/vite-plugin"; import basicSsl from "@vitejs/plugin-basic-ssl"; export default defineConfig({ - // plugins: [react()], plugins: [react(), basicSsl()], server: { https: {}, @@ -11,6 +11,29 @@ export default defineConfig({ host: "0.0.0.0", open: true, }, + css: { + modules: { + generateScopedName: () => { + if (process.env.NODE_ENV === "production") { + return `[hash:base64:8]`; + } + return `[name]__[local]___[hash:base64:5]`; + }, + }, + preprocessorOptions: { + less: { + // 全局变量 + // additionalData: `@import "${resolve(__dirname, "src/styles/variables.less")}";`, + // Less 选项 + javascriptEnabled: true, + modifyVars: { + // 可以在这里定义全局变量 + "@primary-color": "#1890ff", + "@border-radius-base": "6px", + }, + }, + }, + }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), @@ -19,6 +42,17 @@ export default defineConfig({ "@utils": path.resolve(__dirname, "../../packages/utils/src"), }, }, + build: { + rollupOptions: { + output: { + manualChunks: { + vendor: ["react", "react-dom"], + router: ["react-router-dom"], + }, + }, + }, + chunkSizeWarningLimit: 1000, + }, optimizeDeps: { include: ["@shared", "@hooks", "@utils"], }, diff --git a/src/api/mock.ts b/src/api/mock.ts deleted file mode 100644 index 0e65ac0..0000000 --- a/src/api/mock.ts +++ /dev/null @@ -1,37 +0,0 @@ -import useAxios from 'axios-hooks'; -import {Page, Result} from "@/types/http"; - -export interface MockResult { - id: number; -} - -export interface MockPage { - id: number; -} - -/** - * fetch the data - * 详细使用可以查看 useAxios 的文档 - */ -export const useFetchXXX = () => { - // set the url - const url = `/xxx/xxx`; - // fetch the data - const [{data, loading, error}, refetch] = useAxios>(url); - // to do something - return {data, loading, error, refetch}; -} - - -/** - * fetch the data with page - * 详细使用可以查看 useAxios 的文档 - */ -export const useFetchPageXXX = (page: number, size: number) => { - // set the url - const url = `/xxx/xxx?page=${page}&size=${size}`; - // fetch the data - const [{data, loading, error}, refetch] = useAxios>(url); - // to do something - return {data, loading, error, refetch}; -} \ No newline at end of file diff --git a/src/assets/translate/cat.svg b/src/assets/translate/cat.svg deleted file mode 100644 index 3457da1..0000000 --- a/src/assets/translate/cat.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/translate/dog.svg b/src/assets/translate/dog.svg deleted file mode 100644 index aa4b8b8..0000000 --- a/src/assets/translate/dog.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/translate/microphone.svg b/src/assets/translate/microphone.svg deleted file mode 100644 index 6499bd9..0000000 --- a/src/assets/translate/microphone.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/assets/translate/microphoneDisabledSvg.svg b/src/assets/translate/microphoneDisabledSvg.svg deleted file mode 100644 index 463ff15..0000000 --- a/src/assets/translate/microphoneDisabledSvg.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/assets/translate/pig.svg b/src/assets/translate/pig.svg deleted file mode 100644 index b1ece1b..0000000 --- a/src/assets/translate/pig.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/translate/playing.svg b/src/assets/translate/playing.svg deleted file mode 100644 index 191166a..0000000 --- a/src/assets/translate/playing.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/component/audioRecorder/archives.tsx b/src/component/audioRecorder/archives.tsx deleted file mode 100644 index 1a09fbe..0000000 --- a/src/component/audioRecorder/archives.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function Index() { - return <>档案; -} - -export default Index; diff --git a/src/component/audioRecorder/deviceCompatibility.tsx b/src/component/audioRecorder/deviceCompatibility.tsx deleted file mode 100644 index a599eb7..0000000 --- a/src/component/audioRecorder/deviceCompatibility.tsx +++ /dev/null @@ -1,139 +0,0 @@ -// components/DeviceCompatibility.tsx -import React, { useEffect, useState } from "react"; -import { UniversalAudioRecorder } from "@/utils/audioRecorder"; - -interface DeviceInfo { - isIOS: boolean; - isSafari: boolean; - supportedFormats: string[]; - hasMediaRecorder: boolean; - hasGetUserMedia: boolean; -} - -const DeviceCompatibility: React.FC = () => { - const [deviceInfo, setDeviceInfo] = useState(null); - const [showDetails, setShowDetails] = useState(false); - - useEffect(() => { - const checkCompatibility = () => { - const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); - const isSafari = /^((?!chrome|android).)*safari/i.test( - navigator.userAgent - ); - const hasMediaRecorder = typeof MediaRecorder !== "undefined"; - const hasGetUserMedia = !!( - navigator.mediaDevices && navigator.mediaDevices.getUserMedia - ); - - let supportedFormats: string[] = []; - if (hasMediaRecorder) { - supportedFormats = UniversalAudioRecorder.getSupportedFormats(); - } - - setDeviceInfo({ - isIOS, - isSafari, - supportedFormats, - hasMediaRecorder, - hasGetUserMedia, - }); - }; - - checkCompatibility(); - }, []); - - if (!deviceInfo) return null; - - const getCompatibilityStatus = (): "good" | "warning" | "error" => { - if (!deviceInfo.hasMediaRecorder || !deviceInfo.hasGetUserMedia) { - return "error"; - } - if (deviceInfo.supportedFormats.length === 0) { - return "warning"; - } - return "good"; - }; - - const status = getCompatibilityStatus(); - - return ( -
-
setShowDetails(!showDetails)} - > - - {status === "good" && "✅"} - {status === "warning" && "⚠️"} - {status === "error" && "❌"} - - - {status === "good" && "设备兼容"} - {status === "warning" && "部分兼容"} - {status === "error" && "不兼容"} - - {showDetails ? "▼" : "▶"} -
- - {showDetails && ( -
-
-

- 设备信息: -

-
    -
  • iOS设备: {deviceInfo.isIOS ? "是" : "否"}
  • -
  • Safari浏览器: {deviceInfo.isSafari ? "是" : "否"}
  • -
  • - 支持MediaRecorder: {deviceInfo.hasMediaRecorder ? "是" : "否"} -
  • -
  • - 支持getUserMedia: {deviceInfo.hasGetUserMedia ? "是" : "否"} -
  • -
-
- -
-

- 支持的音频格式: -

- {deviceInfo.supportedFormats.length > 0 ? ( -
    - {deviceInfo.supportedFormats.map((format, index) => ( -
  • {format}
  • - ))} -
- ) : ( -

未检测到支持的格式

- )} -
- - {status === "error" && ( -
-

- 错误: 您的设备不支持录音功能 -

-

请尝试:

-
    -
  • 使用最新版本的浏览器
  • -
  • 确保在HTTPS环境下访问
  • -
  • 检查浏览器权限设置
  • -
-
- )} - - {status === "warning" && ( -
-

- 警告: 录音功能可能不稳定 -

-

建议使用Chrome、Safari或Firefox最新版本

-
- )} -
- )} -
- ); -}; - -export default DeviceCompatibility; diff --git a/src/component/audioRecorder/index copy.tsx b/src/component/audioRecorder/index copy.tsx deleted file mode 100644 index ad1c8ff..0000000 --- a/src/component/audioRecorder/index copy.tsx +++ /dev/null @@ -1,278 +0,0 @@ -// components/PetTranslatorChat.tsx (添加音频控制) -import React, { useRef, useEffect } from "react"; -import { usePetTranslator } from "@/hooks/usePetTranslator"; -import { useFileUpload } from "@/hooks/useFileUpload"; -import { useAudioControl } from "@/hooks/useAudioControl"; -import { VoiceMessage, ChatMessage } from "@/types/chat"; -import { UploadConfig } from "@/types/upload"; -import VoiceMessageComponent from "./voiceMessage"; -import VoiceRecordButton from "./voiceRecordButton"; -import RecordingStatusBar from "./recordingStatusBar"; -import AudioManager from "@/utils/audioManager"; -import { useVoiceRecorder } from "@/hooks/useVoiceRecorder"; -import "./index.less"; -import { CapsuleTabs } from "antd-mobile"; - -const PetTranslatorChat: React.FC = () => { - const { - messages, - currentPet, - translateVoice, - addMessage, - updateMessage, - clearMessages, - } = usePetTranslator(); - - const { isRecording, isPaused, recordingTime } = useVoiceRecorder(); - const { uploadFile } = useFileUpload(); - const { currentPlayingId, stopAllAudio, pauseAllAudio } = useAudioControl(); - const messagesEndRef = useRef(null); - - // 上传配置 - const uploadConfig: UploadConfig = { - url: "/api/upload/voice", - method: "POST", - fieldName: "voiceFile", - maxFileSize: 10 * 1024 * 1024, // 10MB - allowedTypes: [ - "audio/webm", - "audio/mp4", - "audio/aac", - "audio/wav", - "audio/ogg", - "audio/mpeg", - "audio/x-m4a", - ], - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }; - - // 组件卸载时清理所有音频 - useEffect(() => { - return () => { - AudioManager.getInstance().cleanup(); - }; - }, []); - - // 开始录音时暂停所有音频播放 - useEffect(() => { - if (isRecording) { - pauseAllAudio(); - } - }, [isRecording, pauseAllAudio]); - - const handleRecordComplete = ( - audioBlob: Blob, - duration: number, - uploadResponse?: any - ) => { - const audioUrl = URL.createObjectURL(audioBlob); - - const voiceMessage: VoiceMessage = { - id: `voice_${Date.now()}`, - type: "voice", - content: { - duration, - url: audioUrl, - blob: audioBlob, - uploadStatus: uploadResponse ? "success" : undefined, - fileId: uploadResponse?.data?.fileId, - fileName: uploadResponse?.data?.fileName, - serverUrl: uploadResponse?.data?.fileUrl, - }, - sender: "user", - timestamp: Date.now(), - }; - - addMessage(voiceMessage); - - // 自动开始翻译 - setTimeout(() => { - translateVoice(voiceMessage); - }, 500); - }; - - const handleRetryUpload = async (messageId: string) => { - const message = messages.find( - (msg) => msg.id === messageId - ) as VoiceMessage; - if (!message || !message.content.blob) return; - - try { - // 更新上传状态 - updateMessage(messageId, { - ...message, - content: { - ...message.content, - uploadStatus: "uploading", - uploadProgress: 0, - }, - }); - - const fileName = `voice_${Date.now()}.wav`; - const uploadResponse = await uploadFile( - message.content.blob, - fileName, - uploadConfig - ); - - // 更新成功状态 - updateMessage(messageId, { - ...message, - content: { - ...message.content, - uploadStatus: "success", - fileId: uploadResponse.data?.fileId, - fileName: uploadResponse.data?.fileName, - serverUrl: uploadResponse.data?.fileUrl, - }, - }); - } catch (error) { - // 更新失败状态 - updateMessage(messageId, { - ...message, - content: { - ...message.content, - uploadStatus: "error", - }, - }); - - console.error("重新上传失败:", error); - } - }; - - const handleRecordError = (error: Error) => { - alert(error.message); - }; - - const formatTime = (timestamp: number): string => { - return new Date(timestamp).toLocaleTimeString("zh-CN", { - hour: "2-digit", - minute: "2-digit", - }); - }; - - return ( - <> - {/* 录音状态栏 */} - - - {/* 头部 */} - {/*
-
-
{currentPet.avatar}
-
-
{currentPet.name}
-
- {isRecording - ? "正在听你说话..." - : currentPlayingId - ? "正在播放语音..." - : "在线 · 等待翻译"} -
-
-
*/} - - {/*
- - {currentPlayingId && ( - - )} - - */} - {/*
-
*/} -
- - - 宠物翻译 - - - 宠物档案 - - -
- - {/* 消息列表 */} -
- {messages.length === 0 ? ( -
-
🐾
-
开始和{currentPet.name}对话吧!
-
- 点击下方按钮开始录音,我会帮你翻译 -
-
- ) : ( - messages.map((message) => ( -
-
- {message.sender === "pet" && ( -
{currentPet.avatar}
- )} - -
- {message.type === "voice" ? ( - translateVoice(message)} - onRetryUpload={() => handleRetryUpload(message.id)} - /> - ) : ( -
{message.content}
- )} - -
- {formatTime(message.timestamp)} -
-
- - {message.sender === "user" && ( -
👤
- )} -
-
- )) - )} -
-
- - {/* 输入区域 */} - - - ); -}; - -export default PetTranslatorChat; diff --git a/src/component/audioRecorder/index.less b/src/component/audioRecorder/index.less deleted file mode 100644 index d8a03a5..0000000 --- a/src/component/audioRecorder/index.less +++ /dev/null @@ -1,774 +0,0 @@ -/* PetTranslatorChat.css */ -.pet-translator-chat { - display: flex; - flex-direction: column; - height: 100vh; - max-width: 400px; - margin: 0 auto; - background: #f5f5f5; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -} - -/* 头部样式 */ -.chat-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 16px; - background: #fff; - color: rgba(0, 0, 0, 0.25); - .lef { - display: flex; - gap: 12px; - - h2 { - font-size: 20px; - &.active { - color: #000; - } - } - } -} - -.pet-info { - display: flex; - align-items: center; - gap: 12px; -} - -.pet-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - background: #f0f0f0; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; -} - -.pet-name { - font-weight: 600; - color: #333; - font-size: 16px; -} - -.pet-status { - font-size: 12px; - color: #999; -} - -.clear-button { - background: none; - border: none; - font-size: 18px; - cursor: pointer; - padding: 8px; - border-radius: 50%; - transition: background 0.3s; -} - -.clear-button:hover { - background: #f0f0f0; -} - -/* 消息容器 */ -.messages-container { - flex: 1; - overflow-y: auto; - padding: 16px; - display: flex; - flex-direction: column; - gap: 16px; -} - -/* 空状态 */ -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - text-align: center; - color: #999; -} - -.empty-icon { - font-size: 48px; - margin-bottom: 16px; -} - -.empty-title { - font-size: 18px; - font-weight: 600; - margin-bottom: 8px; - color: #666; -} - -.empty-subtitle { - font-size: 14px; - line-height: 1.4; -} - -/* 消息样式 */ -.message { - display: flex; - gap: 8px; - max-width: 80%; -} - -.message.own { - align-self: flex-end; - flex-direction: row-reverse; -} - -.message.other { - align-self: flex-start; -} - -.avatar { - width: 32px; - height: 32px; - border-radius: 50%; - background: #f0f0f0; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - flex-shrink: 0; -} - -.user-avatar { - background: #007bff; - color: white; -} - -.message-content { - display: flex; - flex-direction: column; - gap: 4px; -} - -.message-time { - font-size: 11px; - color: #999; - text-align: center; -} - -/* 语音消息样式 */ -.voice-message { - background: #fff; - border-radius: 18px; - padding: 12px 16px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - min-width: 120px; -} - -.voice-message.own { - background: #007bff; - color: white; -} - -.voice-content { - display: flex; - align-items: center; - gap: 8px; -} - -.play-button { - width: 24px; - height: 24px; - border: none; - background: none; - cursor: pointer; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - transition: background 0.3s; -} - -.play-button:hover { - background: rgba(0, 0, 0, 0.1); -} - -.voice-message.own .play-button:hover { - background: rgba(255, 255, 255, 0.2); -} - -.waveform { - display: flex; - align-items: center; - gap: 2px; - height: 20px; - flex: 1; -} - -.waveform-bar { - width: 3px; - background: #ddd; - border-radius: 2px; - transition: all 0.3s; -} - -.voice-message.own .waveform-bar { - background: rgba(255, 255, 255, 0.6); -} - -.waveform-bar.active { - background: #007bff; -} - -.voice-message.own .waveform-bar.active { - background: white; -} - -.duration { - font-size: 12px; - color: #666; - min-width: 30px; - text-align: right; -} - -.voice-message.own .duration { - color: rgba(255, 255, 255, 0.8); -} - -/* 翻译部分 */ -.translation-section { - margin-top: 8px; - padding-top: 8px; - border-top: 1px solid #eee; -} - -.translate-button { - background: #28a745; - color: white; - border: none; - border-radius: 12px; - padding: 6px 12px; - font-size: 12px; - cursor: pointer; - transition: background 0.3s; -} - -.translate-button:hover { - background: #218838; -} - -.translating { - display: flex; - align-items: center; - gap: 8px; - color: #666; - font-size: 12px; -} - -.loading-dots { - display: flex; - gap: 2px; -} - -.loading-dots span { - width: 4px; - height: 4px; - background: #666; - border-radius: 50%; - animation: loading 1.4s infinite ease-in-out; -} - -.loading-dots span:nth-child(1) { - animation-delay: -0.32s; -} -.loading-dots span:nth-child(2) { - animation-delay: -0.16s; -} - -@keyframes loading { - 0%, - 80%, - 100% { - transform: scale(0); - } - 40% { - transform: scale(1); - } -} - -.translation-result { - display: flex; - align-items: flex-start; - gap: 8px; - background: rgba(40, 167, 69, 0.1); - padding: 8px; - border-radius: 8px; - border-left: 3px solid #28a745; -} - -.translation-icon { - font-size: 14px; -} - -.translation-text { - font-size: 13px; - line-height: 1.4; - color: #333; -} - -/* 文本消息 */ -.text-message { - background: #fff; - border-radius: 18px; - padding: 12px 16px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - font-size: 14px; - line-height: 1.4; -} - -.message.other .text-message { - background: #e8f5e8; -} - -/* 输入区域 */ -.input-area { - padding: 16px; - background: #fff; - border-top: 1px solid #e5e5e5; -} - -/* 录音按钮 */ -.voice-record-container { - position: relative; -} - -.voice-record-button { - width: 100%; - height: 50px; - background: #007bff; - color: white; - border: none; - border-radius: 25px; - font-size: 16px; - cursor: pointer; - transition: all 0.3s; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - user-select: none; -} - -.voice-record-button:hover { - background: #0056b3; -} - -.voice-record-button.pressed { - background: #dc3545; - transform: scale(0.95); -} - -.microphone-icon { - font-size: 18px; -} - -.button-text { - font-weight: 500; -} - -/* 录音覆盖层 */ -.recording-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.recording-modal { - background: white; - border-radius: 20px; - padding: 40px; - text-align: center; - max-width: 300px; - width: 90%; -} - -.recording-animation { - margin-bottom: 20px; -} - -.sound-wave { - display: flex; - justify-content: center; - align-items: center; - gap: 4px; - height: 40px; -} - -.wave-bar { - width: 4px; - background: #007bff; - border-radius: 2px; - animation: wave 1s infinite ease-in-out; -} - -.wave-bar:nth-child(1) { - animation-delay: -0.4s; -} -.wave-bar:nth-child(2) { - animation-delay: -0.3s; -} -.wave-bar:nth-child(3) { - animation-delay: -0.2s; -} -.wave-bar:nth-child(4) { - animation-delay: -0.1s; -} -.wave-bar:nth-child(5) { - animation-delay: 0s; -} - -@keyframes wave { - 0%, - 40%, - 100% { - height: 10px; - } - 20% { - height: 30px; - } -} - -.recording-time { - font-size: 24px; - font-weight: 600; - color: #333; - margin-bottom: 16px; -} - -.recording-hint { - font-size: 14px; - color: #666; -} - -.cancel-hint { - color: #dc3545; - font-weight: 600; -} - -.normal-hint { - color: #666; -} - -/* 响应式 */ -@media (max-width: 480px) { - .pet-translator-chat { - height: 100vh; - } - - .message { - max-width: 85%; - } - - .recording-modal { - padding: 30px 20px; - } -} - -/* 上传进度样式 */ -.upload-progress { - margin: 12px 0; - padding: 8px; - background: rgba(0, 123, 255, 0.1); - border-radius: 8px; - border: 1px solid rgba(0, 123, 255, 0.2); -} - -.upload-status { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; - font-size: 13px; - color: #007bff; -} - -.upload-icon { - font-size: 14px; -} - -.upload-progress-bar { - width: 100%; - height: 4px; - background: rgba(0, 123, 255, 0.2); - border-radius: 2px; - overflow: hidden; -} - -.upload-progress-fill { - height: 100%; - background: #007bff; - border-radius: 2px; - transition: width 0.3s ease; -} - -/* 语音消息上传状态 */ -.upload-status-section { - margin-top: 8px; - padding-top: 8px; - border-top: 1px solid rgba(255, 255, 255, 0.2); -} - -.voice-message.own .upload-status-section { - border-top-color: rgba(255, 255, 255, 0.3); -} - -.upload-status-indicator { - display: flex; - align-items: center; - gap: 6px; - font-size: 11px; - margin-bottom: 4px; -} - -.upload-status-indicator.uploading { - color: #007bff; -} - -.upload-status-indicator.success { - color: #28a745; -} - -.upload-status-indicator.error { - color: #dc3545; -} - -.retry-upload-button { - background: #dc3545; - color: white; - border: none; - border-radius: 4px; - padding: 2px 6px; - font-size: 10px; - cursor: pointer; - margin-left: 4px; - transition: background 0.3s; -} - -.retry-upload-button:hover { - background: #c82333; -} - -.file-info { - display: flex; - flex-direction: column; - gap: 2px; - font-size: 10px; - color: rgba(255, 255, 255, 0.7); -} - -.voice-message:not(.own) .file-info { - color: #666; -} - -.file-name, -.file-size { - display: flex; - align-items: center; - gap: 4px; -} - -/* 处理中状态 */ -.processing-hint { - color: #007bff; - font-weight: 600; -} - -.upload-hint { - color: #28a745; - font-weight: 600; -} - -/* 按钮禁用状态 */ -.control-button:disabled { - opacity: 0.5; - cursor: not-allowed; - transform: none !important; -} - -.voice-start-button:disabled { - opacity: 0.7; - cursor: not-allowed; - transform: none !important; -} - -/* 上传动画 */ -@keyframes uploadPulse { - 0% { - opacity: 1; - } - 50% { - opacity: 0.6; - } - 100% { - opacity: 1; - } -} - -.upload-status-indicator.uploading .upload-icon { - animation: uploadPulse 1s infinite; -} - -/* DeviceCompatibility.css */ -.device-compatibility { - margin: 10px 0; - border-radius: 8px; - overflow: hidden; - font-size: 12px; -} - -.device-compatibility.good { - background: #d4edda; - border: 1px solid #c3e6cb; -} - -.device-compatibility.warning { - background: #fff3cd; - border: 1px solid #ffeaa7; -} - -.device-compatibility.error { - background: #f8d7da; - border: 1px solid #f5c6cb; -} - -.compatibility-header { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - cursor: pointer; - user-select: none; -} - -.status-icon { - font-size: 14px; -} - -.status-text { - flex: 1; - font-weight: 600; -} - -.toggle-icon { - font-size: 10px; - transition: transform 0.3s; -} - -.compatibility-details { - padding: 12px; - border-top: 1px solid rgba(0, 0, 0, 0.1); - background: rgba(255, 255, 255, 0.5); -} - -.device-info ul, -.supported-formats ul { - margin: 8px 0; - padding-left: 20px; -} - -.device-info li, -.supported-formats li { - margin: 4px 0; -} - -.no-formats { - color: #dc3545; - font-style: italic; -} - -.error-message, -.warning-message { - margin-top: 12px; - padding: 8px; - border-radius: 4px; -} - -.error-message { - background: rgba(220, 53, 69, 0.1); - border-left: 3px solid #dc3545; -} - -.warning-message { - background: rgba(255, 193, 7, 0.1); - border-left: 3px solid #ffc107; -} - -/* 播放错误样式 */ -.play-error { - display: flex; - align-items: center; - gap: 6px; - margin-top: 8px; - padding: 6px 8px; - background: rgba(220, 53, 69, 0.1); - border: 1px solid rgba(220, 53, 69, 0.3); - border-radius: 6px; - font-size: 11px; -} - -.error-icon { - color: #dc3545; -} - -.error-text { - flex: 1; - color: #dc3545; -} - -.retry-play-button { - background: #dc3545; - color: white; - border: none; - border-radius: 4px; - padding: 2px 6px; - font-size: 10px; - cursor: pointer; - transition: background 0.3s; -} - -.retry-play-button:hover { - background: #c82333; -} - -.play-button.error { - background: rgba(220, 53, 69, 0.1); - color: #dc3545; -} - -.play-button:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -/* 安卓特定样式优化 */ -@media screen and (max-width: 768px) { - .voice-content { - min-height: 40px; - } - - .play-button { - min-width: 32px; - min-height: 32px; - } - - .waveform { - min-height: 24px; - } -} diff --git a/src/component/audioRecorder/index.tsx b/src/component/audioRecorder/index.tsx deleted file mode 100644 index cd86b08..0000000 --- a/src/component/audioRecorder/index.tsx +++ /dev/null @@ -1,275 +0,0 @@ -// components/PetTranslatorChat.tsx (添加音频控制) -import React, { useRef, useEffect, useState } from "react"; -import { usePetTranslator } from "@/hooks/usePetTranslator"; -import { useFileUpload } from "@/hooks/useFileUpload"; -import { useAudioControl } from "@/hooks/useAudioControl"; -import { VoiceMessage, ChatMessage } from "@/types/chat"; -import { UploadConfig } from "@/types/upload"; -import VoiceMessageComponent from "./voiceMessage"; -import VoiceRecordButton from "./voiceRecordButton"; -import RecordingStatusBar from "./recordingStatusBar"; -import AudioManager from "@/utils/audioManager"; -import { useVoiceRecorder } from "@/hooks/useVoiceRecorder"; -import TranslateItem from "./translateItem"; -import ArchivesItem from "./archives"; -import "./index.less"; -import { CapsuleTabs } from "antd-mobile"; - -const PetTranslatorChat: React.FC = () => { - const { - messages, - currentPet, - translateVoice, - addMessage, - updateMessage, - clearMessages, - } = usePetTranslator(); - - const { isRecording, isPaused, recordingTime } = useVoiceRecorder(); - const { uploadFile } = useFileUpload(); - const { currentPlayingId, stopAllAudio, pauseAllAudio } = useAudioControl(); - const messagesEndRef = useRef(null); - const [tabValue, setTabValue] = useState("translate"); - - // 上传配置 - const uploadConfig: UploadConfig = { - url: "/api/upload/voice", - method: "POST", - fieldName: "voiceFile", - maxFileSize: 10 * 1024 * 1024, // 10MB - allowedTypes: [ - "audio/webm", - "audio/mp4", - "audio/aac", - "audio/wav", - "audio/ogg", - "audio/mpeg", - "audio/x-m4a", - ], - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }; - - // 组件卸载时清理所有音频 - useEffect(() => { - return () => { - AudioManager.getInstance().cleanup(); - }; - }, []); - - // 开始录音时暂停所有音频播放 - useEffect(() => { - if (isRecording) { - pauseAllAudio(); - } - }, [isRecording, pauseAllAudio]); - - const handleRecordComplete = ( - audioBlob: Blob, - duration: number, - uploadResponse?: any - ) => { - const audioUrl = URL.createObjectURL(audioBlob); - - const voiceMessage: VoiceMessage = { - id: `voice_${Date.now()}`, - type: "voice", - content: { - duration, - url: audioUrl, - blob: audioBlob, - uploadStatus: uploadResponse ? "success" : undefined, - fileId: uploadResponse?.data?.fileId, - fileName: uploadResponse?.data?.fileName, - serverUrl: uploadResponse?.data?.fileUrl, - }, - sender: "user", - timestamp: Date.now(), - }; - - addMessage(voiceMessage); - - // 自动开始翻译 - setTimeout(() => { - translateVoice(voiceMessage); - }, 500); - }; - - const handleRetryUpload = async (messageId: string) => { - const message = messages.find( - (msg) => msg.id === messageId - ) as VoiceMessage; - if (!message || !message.content.blob) return; - - try { - // 更新上传状态 - updateMessage(messageId, { - ...message, - content: { - ...message.content, - uploadStatus: "uploading", - uploadProgress: 0, - }, - }); - - const fileName = `voice_${Date.now()}.wav`; - const uploadResponse = await uploadFile( - message.content.blob, - fileName, - uploadConfig - ); - - // 更新成功状态 - updateMessage(messageId, { - ...message, - content: { - ...message.content, - uploadStatus: "success", - fileId: uploadResponse.data?.fileId, - fileName: uploadResponse.data?.fileName, - serverUrl: uploadResponse.data?.fileUrl, - }, - }); - } catch (error) { - // 更新失败状态 - updateMessage(messageId, { - ...message, - content: { - ...message.content, - uploadStatus: "error", - }, - }); - - console.error("重新上传失败:", error); - } - }; - - const handleRecordError = (error: Error) => { - alert(error.message); - }; - - const formatTime = (timestamp: number): string => { - return new Date(timestamp).toLocaleTimeString("zh-CN", { - hour: "2-digit", - minute: "2-digit", - }); - }; - - return ( - <> - {/* 录音状态栏 */} - - - {/* 头部 */} -
-
-

setTabValue("translate")} - className={`${tabValue === "translate" ? "active" : ""}`} - > - 宠物翻译 -

-

setTabValue("archives")} - className={`${tabValue === "archives" ? "active" : ""}`} - > - 宠物档案 -

-
- - {/*
-
{currentPet.avatar}
*/} - {/*
*/} - {/*
{currentPet.name}
*/} - {/*
- {isRecording - ? "正在听你说话..." - : currentPlayingId - ? "正在播放语音..." - : "在线 · 等待翻译"} -
*/} - {/*
*/} - {/*
*/} - - {/*
- {currentPlayingId && ( - - )} - - -
*/} -
- {tabValue == "translate" ? : } - {/* 消息列表 */} -
- {messages.map((message) => ( -
-
- {message.sender === "pet" && ( -
{currentPet.avatar}
- )} - -
- {message.type === "voice" ? ( - translateVoice(message)} - onRetryUpload={() => handleRetryUpload(message.id)} - /> - ) : ( -
{message.content}
- )} - -
- {formatTime(message.timestamp)} -
-
- - {message.sender === "user" && ( -
👤
- )} -
-
- ))} -
-
- - {/* 输入区域 */} - - - ); -}; - -export default PetTranslatorChat; diff --git a/src/component/audioRecorder/recordingStatusBar.tsx b/src/component/audioRecorder/recordingStatusBar.tsx deleted file mode 100644 index e7d6df0..0000000 --- a/src/component/audioRecorder/recordingStatusBar.tsx +++ /dev/null @@ -1,54 +0,0 @@ -// components/RecordingStatusBar.tsx -import React from "react"; - -interface RecordingStatusBarProps { - isRecording: boolean; - isPaused: boolean; - duration: number; - maxDuration: number; -} - -const RecordingStatusBar: React.FC = ({ - isRecording, - isPaused, - duration, - maxDuration, -}) => { - if (!isRecording) return null; - - const formatTime = (seconds: number): string => { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins.toString().padStart(2, "0")}:${secs - .toString() - .padStart(2, "0")}`; - }; - - const getStatusColor = (): string => { - if (isPaused) return "#ffc107"; - if (duration >= maxDuration * 0.9) return "#dc3545"; - return "#28a745"; - }; - - return ( -
-
-
-
- - {isPaused ? "录音暂停中" : "正在录音"} - -
- -
- {formatTime(duration)} / {formatTime(maxDuration)} -
-
-
- ); -}; - -export default RecordingStatusBar; diff --git a/src/component/audioRecorder/translateItem.tsx b/src/component/audioRecorder/translateItem.tsx deleted file mode 100644 index ec6390b..0000000 --- a/src/component/audioRecorder/translateItem.tsx +++ /dev/null @@ -1,11 +0,0 @@ -function Index() { - return <> - - - - - - ; -} - -export default Index; diff --git a/src/component/audioRecorder/voiceMessage.tsx b/src/component/audioRecorder/voiceMessage.tsx deleted file mode 100644 index 3b684d3..0000000 --- a/src/component/audioRecorder/voiceMessage.tsx +++ /dev/null @@ -1,322 +0,0 @@ -// 消息列表 -import React, { useState, useRef, useEffect } from "react"; -import { VoiceMessage as VoiceMessageType } from "@/types/chat"; -import { UniversalAudioPlayer } from "@/utils/audioPlayer"; -import AudioManager from "@/utils/audioManager"; - -interface VoiceMessageProps { - message: VoiceMessageType; - isOwn: boolean; - onTranslate?: () => void; - onRetryUpload?: () => void; -} - -const VoiceMessage: React.FC = ({ - message, - isOwn, - onTranslate, - onRetryUpload, -}) => { - const [isPlaying, setIsPlaying] = useState(false); - const [currentTime, setCurrentTime] = useState(0); - const [isLoading, setIsLoading] = useState(false); - const [playError, setPlayError] = useState(null); - - const playerRef = useRef(null); - const timerRef = useRef(null); - const audioManager = AudioManager.getInstance(); - - // 使用消息ID作为音频实例的唯一标识 - const audioId = `voice_${message.id}`; - - useEffect(() => { - return () => { - // 组件卸载时清理 - stopTimer(); - audioManager.unregisterAudio(audioId); - }; - }, [audioId]); - - const startTimer = () => { - timerRef.current = setInterval(() => { - if (audioManager.isPlaying(audioId)) { - const current = audioManager.getCurrentTime(audioId); - setCurrentTime(current); - } else { - // 播放结束 - stopTimer(); - setIsPlaying(false); - setCurrentTime(0); - } - }, 100); - }; - - const stopTimer = () => { - if (timerRef.current) { - clearInterval(timerRef.current); - timerRef.current = null; - } - }; - - const loadAudio = async (): Promise => { - if (playerRef.current) { - return; // 已经加载过了 - } - - setIsLoading(true); - setPlayError(null); - - try { - const player = new UniversalAudioPlayer(); - - // 优先使用blob,其次使用URL - let audioBlob: Blob | null = null; - - if (message.content.blob) { - audioBlob = message.content.blob; - } else if (message.content.url || message.content.serverUrl) { - // 从URL获取blob - const audioUrl = message.content.serverUrl || message.content.url; - const response = await fetch(audioUrl!); - audioBlob = await response.blob(); - } - - if (!audioBlob) { - throw new Error("无法获取音频数据"); - } - - await player.loadAudio(audioBlob); - playerRef.current = player; - - // 注册到全局音频管理器 - audioManager.registerAudio(audioId, player, { - onPlay: () => { - setIsPlaying(true); - startTimer(); - }, - onPause: () => { - setIsPlaying(false); - stopTimer(); - }, - onStop: () => { - setIsPlaying(false); - setCurrentTime(0); - stopTimer(); - }, - }); - - console.log("音频加载成功:", { - id: audioId, - size: audioBlob.size, - type: audioBlob.type, - duration: player.getDuration(), - }); - } catch (error) { - console.error("音频加载失败:", error); - setPlayError(error instanceof Error ? error.message : "音频加载失败"); - } finally { - setIsLoading(false); - } - }; - - const togglePlay = async () => { - try { - if (!playerRef.current) { - await loadAudio(); - if (!playerRef.current) { - return; - } - } - - if (isPlaying) { - // 暂停当前音频 - audioManager.pauseAudio(audioId); - } else { - // 播放音频(会自动停止其他正在播放的音频) - await audioManager.playAudio(audioId); - setPlayError(null); - } - } catch (error) { - console.error("播放控制失败:", error); - setPlayError(error instanceof Error ? error.message : "播放失败"); - setIsPlaying(false); - stopTimer(); - } - }; - - const formatTime = (seconds: number): string => { - return `${Math.floor(seconds)}''`; - }; - - const formatFileSize = (bytes: number): string => { - if (bytes === 0) return "0 B"; - const k = 1024; - const sizes = ["B", "KB", "MB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]; - }; - - const getWaveformBars = (): JSX.Element[] => { - const bars = []; - const barCount = Math.min( - Math.max(Math.floor(message.content.duration), 3), - 20 - ); - const duration = - audioManager.getDuration(audioId) || message.content.duration; - - for (let i = 0; i < barCount; i++) { - const height = Math.random() * 20 + 10; - const isActive = - isPlaying && duration > 0 && (currentTime / duration) * barCount > i; - - bars.push( -
- ); - } - - return bars; - }; - - const getUploadStatusIcon = () => { - switch (message.content.uploadStatus) { - case "uploading": - return "📤"; - case "success": - return "✅"; - case "error": - return "❌"; - default: - return null; - } - }; - - const getPlayButtonContent = () => { - if (isLoading) return "⏳"; - if (playError) return "❌"; - if (isPlaying) return "⏸️"; - return "▶️"; - }; - - const isPlayDisabled = - isLoading || - (!message.content.url && - !message.content.serverUrl && - !message.content.blob); - - return ( -
-
- - -
{getWaveformBars()}
- - - {formatTime(isPlaying ? currentTime : message.content.duration)} - -
- - {/* 播放错误提示 */} - {playError && ( -
- ⚠️ - {playError} - -
- )} - - {/* 上传状态显示 */} - {isOwn && message.content.uploadStatus && ( -
-
- {getUploadStatusIcon()} - - {message.content.uploadStatus === "uploading" && - `上传中 ${message.content.uploadProgress || 0}%`} - {message.content.uploadStatus === "success" && "已上传"} - {message.content.uploadStatus === "error" && "上传失败"} - - - {message.content.uploadStatus === "error" && onRetryUpload && ( - - )} -
- - {/* 文件信息 */} - {message.content.uploadStatus === "success" && ( -
- {message.content.fileName && ( - 📁 {message.content.fileName} - )} - {message.content.blob && ( - - 📊 {formatFileSize(message.content.blob.size)} - - )} -
- )} -
- )} - - {/* 翻译按钮和结果 */} - {isOwn && ( -
- {!message.translation && !message.translating && ( - - )} - - {message.translating && ( -
-
- - - -
- 正在翻译中... -
- )} - - {message.translation && ( -
-
🗣️
-
{message.translation}
-
- )} -
- )} -
- ); -}; - -export default VoiceMessage; diff --git a/src/component/audioRecorder/voiceRecordButton.less b/src/component/audioRecorder/voiceRecordButton.less deleted file mode 100644 index 46917f2..0000000 --- a/src/component/audioRecorder/voiceRecordButton.less +++ /dev/null @@ -1,350 +0,0 @@ -/* VoiceRecordButton.css */ -.voice-input-container { - padding: 16px; - background: #fff; - border-top: 1px solid #e5e5e5; -} - -.voice-start-button { - width: 100%; - height: 50px; - background: #007bff; - color: white; - border: none; - border-radius: 25px; - font-size: 16px; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - user-select: none; - box-shadow: 0 2px 8px rgba(0, 123, 255, 0.3); -} - -.voice-start-button:hover { - background: #0056b3; - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 123, 255, 0.4); -} - -.voice-start-button:active { - transform: translateY(0); -} - -.microphone-icon { - font-size: 20px; -} - -.button-text { - font-weight: 600; -} - -/* 录音中的容器 */ -.voice-recording-container { - padding: 20px; - background: #fff; - border-top: 1px solid #e5e5e5; - border-radius: 16px 16px 0 0; - box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1); -} - -/* 录音状态 */ -.recording-status { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.recording-indicator { - display: flex; - align-items: center; - gap: 8px; -} - -.recording-dot { - width: 12px; - height: 12px; - background: #dc3545; - border-radius: 50%; - animation: pulse-recording 1s infinite; -} - -.recording-dot.paused { - background: #ffc107; - animation: none; -} - -@keyframes pulse-recording { - 0% { - transform: scale(1); - opacity: 1; - } - 50% { - transform: scale(1.2); - opacity: 0.7; - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -.recording-text { - font-size: 14px; - font-weight: 600; - color: #333; -} - -.recording-time { - font-size: 18px; - font-weight: 700; - color: #007bff; -} - -.max-time { - font-size: 12px; - color: #999; - font-weight: 400; -} - -/* 进度条 */ -.recording-progress { - width: 100%; - height: 4px; - background: #e9ecef; - border-radius: 2px; - margin-bottom: 20px; - overflow: hidden; -} - -.progress-bar { - height: 100%; - background: linear-gradient(90deg, #007bff, #0056b3); - border-radius: 2px; - transition: width 0.3s ease; -} - -/* 波形动画 */ -.sound-wave-container { - display: flex; - justify-content: center; - margin-bottom: 24px; -} - -.sound-wave { - display: flex; - align-items: center; - gap: 4px; - height: 40px; -} - -.wave-bar { - width: 4px; - background: #007bff; - border-radius: 2px; - animation: wave-animation 1.2s infinite ease-in-out; -} - -.sound-wave.paused .wave-bar { - animation-play-state: paused; -} - -.wave-bar:nth-child(1) { - animation-delay: -0.4s; -} -.wave-bar:nth-child(2) { - animation-delay: -0.3s; -} -.wave-bar:nth-child(3) { - animation-delay: -0.2s; -} -.wave-bar:nth-child(4) { - animation-delay: -0.1s; -} -.wave-bar:nth-child(5) { - animation-delay: 0s; -} - -@keyframes wave-animation { - 0%, - 40%, - 100% { - height: 8px; - opacity: 0.5; - } - 20% { - height: 32px; - opacity: 1; - } -} - -/* 控制按钮 */ -.recording-controls { - display: flex; - justify-content: space-between; - gap: 12px; - margin-bottom: 16px; -} - -.control-button { - flex: 1; - height: 48px; - border: none; - border-radius: 12px; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 2px; - font-size: 12px; - font-weight: 600; - min-width: 0; -} - -.control-button:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.control-button:not(:disabled):hover { - transform: translateY(-2px); -} - -.control-button:not(:disabled):active { - transform: translateY(0); -} - -.cancel-button { - background: #f8f9fa; - color: #dc3545; - border: 2px solid #dc3545; -} - -.cancel-button:not(:disabled):hover { - background: #dc3545; - color: white; -} - -.pause-button { - background: #f8f9fa; - color: #ffc107; - border: 2px solid #ffc107; -} - -.pause-button:not(:disabled):hover { - background: #ffc107; - color: white; -} - -.send-button { - background: #28a745; - color: white; - border: 2px solid #28a745; -} - -.send-button:not(:disabled):hover { - background: #218838; - border-color: #218838; -} - -.button-icon { - font-size: 16px; -} - -.button-label { - font-size: 11px; - line-height: 1; -} - -/* 提示文字 */ -.recording-hint { - text-align: center; - font-size: 13px; - color: #666; -} - -.warning-hint { - color: #dc3545; - font-weight: 600; -} - -.normal-hint { - color: #28a745; -} - -/* 响应式设计 */ -@media (max-width: 480px) { - .voice-recording-container { - padding: 16px; - } - - .recording-controls { - gap: 8px; - } - - .control-button { - height: 44px; - font-size: 11px; - } - - .button-icon { - font-size: 14px; - } - - .button-label { - font-size: 10px; - } - - .recording-time { - font-size: 16px; - } -} - -/* 动画效果 */ -.voice-recording-container { - animation: slideUp 0.3s ease-out; -} - -@keyframes slideUp { - from { - transform: translateY(100%); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -/* 深色模式支持 */ -@media (prefers-color-scheme: dark) { - .voice-recording-container { - background: #2c2c2e; - border-top-color: #3a3a3c; - } - - .recording-text { - color: #fff; - } - - .recording-progress { - background: #3a3a3c; - } - - .wave-bar { - background: #0a84ff; - } - - .control-button { - background: #3a3a3c; - } - - .recording-hint { - color: #8e8e93; - } -} diff --git a/src/component/audioRecorder/voiceRecordButton.tsx b/src/component/audioRecorder/voiceRecordButton.tsx deleted file mode 100644 index 451c1d6..0000000 --- a/src/component/audioRecorder/voiceRecordButton.tsx +++ /dev/null @@ -1,248 +0,0 @@ -// components/VoiceRecordButton.tsx (更新) -import React, { useState, useEffect } from "react"; -import { useVoiceRecorder } from "@/hooks/useVoiceRecorder"; -import { useFileUpload } from "@/hooks/useFileUpload"; -import { UploadConfig } from "@/types/upload"; -import "./index.less"; -interface VoiceRecordButtonProps { - onRecordComplete: ( - audioBlob: Blob, - duration: number, - uploadResponse?: any - ) => void; - onError?: (error: Error) => void; - maxDuration?: number; - uploadConfig?: UploadConfig; - autoUpload?: boolean; -} - -const VoiceRecordButton: React.FC = ({ - onRecordComplete, - onError, - maxDuration = 60, - uploadConfig, - autoUpload = true, -}) => { - const { - isRecording, - recordingTime, - isPaused, - startRecording, - stopRecording, - pauseRecording, - resumeRecording, - cancelRecording, - } = useVoiceRecorder(); - - const { uploadStatus, uploadFile, resetUpload } = useFileUpload(); - const [showControls, setShowControls] = useState(false); - const [isProcessing, setIsProcessing] = useState(false); - - // 自动停止录音当达到最大时长 - useEffect(() => { - if (recordingTime >= maxDuration && isRecording) { - handleSendRecording(); - } - }, [recordingTime, maxDuration, isRecording]); - - const handleStartRecording = async () => { - try { - resetUpload(); - await startRecording(); - setShowControls(true); - } catch (error) { - onError?.(error as Error); - } - }; - - const handleSendRecording = async () => { - if (recordingTime < 1) { - onError?.(new Error("录音时间太短,至少需要1秒")); - cancelRecording(); - setShowControls(false); - return; - } - - setIsProcessing(true); - - try { - const audioBlob = await stopRecording(); - if (!audioBlob) { - throw new Error("录音数据获取失败"); - } - - let uploadResponse; - - // 如果配置了上传且启用自动上传 - if (uploadConfig && autoUpload) { - const fileName = `voice_${Date.now()}.wav`; - uploadResponse = await uploadFile(audioBlob, fileName, uploadConfig); - } - - onRecordComplete(audioBlob, recordingTime, uploadResponse); - setShowControls(false); - } catch (error) { - onError?.(error as Error); - } finally { - setIsProcessing(false); - } - }; - - const handleCancelRecording = () => { - cancelRecording(); - resetUpload(); - setShowControls(false); - }; - - const handlePauseResume = () => { - if (isPaused) { - resumeRecording(); - } else { - pauseRecording(); - } - }; - - const formatTime = (seconds: number): string => { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins.toString().padStart(2, "0")}:${secs - .toString() - .padStart(2, "0")}`; - }; - - const getProgressPercentage = (): number => { - return Math.min((recordingTime / maxDuration) * 100, 100); - }; - - const isUploading = uploadStatus.status === "uploading"; - const uploadProgress = uploadStatus.progress?.percentage || 0; - - if (!showControls) { - return ( -
- -
- ); - } - - return ( -
- {/* 录音状态指示器 */} -
-
-
- - {isProcessing - ? "处理中..." - : isPaused - ? "录音已暂停" - : "正在录音..."} - -
- -
- {formatTime(recordingTime)} - /{formatTime(maxDuration)} -
-
- - {/* 进度条 */} -
-
-
- - {/* 上传进度 */} - {isUploading && ( -
-
- 📤 - 上传中... {uploadProgress}% -
-
-
-
-
- )} - - {/* 波形动画 */} -
-
- {[...Array(5)].map((_, index) => ( -
- ))} -
-
- - {/* 控制按钮 */} -
- - - - - -
- - {/* 提示文字 */} - {/*
- {isProcessing ? ( - 正在处理录音... - ) : isUploading ? ( - 正在上传到服务器... - ) : recordingTime < 1 ? ( - 录音时间至少需要1秒 - ) : ( - 点击发送按钮完成录音 - )} -
*/} -
- ); -}; - -export default VoiceRecordButton; diff --git a/src/component/carousel/index.less b/src/component/carousel/index.less deleted file mode 100644 index f1685d9..0000000 --- a/src/component/carousel/index.less +++ /dev/null @@ -1,7 +0,0 @@ -.carousel { - .carousel-item { - .carousel-image { - border-radius: 15px; - } - } -} \ No newline at end of file diff --git a/src/component/carousel/index.tsx b/src/component/carousel/index.tsx deleted file mode 100644 index 932a50e..0000000 --- a/src/component/carousel/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import Slider, {Settings} from 'react-slick'; -import 'slick-carousel/slick/slick.css'; -import 'slick-carousel/slick/slick-theme.css'; -import './index.less'; - -export interface CarouselComponentProps { - images: string[]; - height?: number; -} - -/** - * 轮播图组件 - * @param images 图片地址数组 - * @param height 图片高度 - * @constructor Carousel - */ -const Carousel: React.FC = ({images, height = 180}) => { - const settings: Settings = { - dots: false, - infinite: true, - speed: 3000, - slidesToShow: 1, - slidesToScroll: 1, - autoplay: true, - autoplaySpeed: 2000, - responsive: [ - { - breakpoint: 768, - settings: { - arrows: false, - } - } - ] - }; - - return ( - - {images.map((image, index) => ( -
- {`Slide -
- ))} -
- ); -}; - -export default Carousel; \ No newline at end of file diff --git a/src/component/floatingMenu/index.less b/src/component/floatingMenu/index.less deleted file mode 100644 index be92e5f..0000000 --- a/src/component/floatingMenu/index.less +++ /dev/null @@ -1,42 +0,0 @@ -/* FloatingFanMenu.css */ - -@keyframes menuItemPop { - 0% { - opacity: 0; - transform: scale(0) rotate(-180deg); - } - 70% { - opacity: 1; - transform: scale(1.1) rotate(-10deg); - } - 100% { - opacity: 1; - transform: scale(1) rotate(0deg); - } -} - -/* 悬停效果 */ -.menu-item:hover { - transform: scale(1.1) !important; - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3) !important; - transition: all 0.2s ease !important; -} -.adm-floating-bubble-button { - z-index: 999; - width: 72px; - height: 72px; - overflow: visible; - .cat { - position: absolute; - width: 70px; - font-size: 12px; - - bottom: -10px; - background: rgba(255, 204, 199, 1); - padding: 4px 0px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - } -} diff --git a/src/component/floatingMenu/index.tsx b/src/component/floatingMenu/index.tsx deleted file mode 100644 index a13588c..0000000 --- a/src/component/floatingMenu/index.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React, { useState, useRef } from "react"; -import { FloatingBubble, Image } from "antd-mobile"; -import { - AddOutline, - MessageOutline, - UserOutline, - SetOutline, - HeartOutline, - CheckOutline, -} from "antd-mobile-icons"; -import { createPortal } from "react-dom"; - -import "./index.less"; -import { MoreTwo } from "@icon-park/react"; - -export interface FloatMenuItemConfig { - icon: React.ReactNode; - type?: string; -} - -const FloatingFanMenu: React.FC<{ - menuItems: FloatMenuItemConfig[]; - value?: FloatMenuItemConfig; - onChange?: (item: FloatMenuItemConfig) => void; -}> = (props) => { - const { menuItems = [] } = props; - const [visible, setVisible] = useState(false); - const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); - const bubbleRef = useRef(null); - - // 点击时获取FloatingBubble的位置 - const handleMainClick = () => { - if (!visible) { - // 显示菜单时,获取当前FloatingBubble的位置 - if (bubbleRef.current) { - const bubble = bubbleRef.current.querySelector( - ".adm-floating-bubble-button" - ); - if (bubble) { - const rect = bubble.getBoundingClientRect(); - setMenuPosition({ - x: rect.left + rect.width / 2, - y: rect.top + rect.height / 2, - }); - } - } - } - setVisible(!visible); - }; - - const handleItemClick = (item: FloatMenuItemConfig) => { - props.onChange?.(item); - setVisible(false); - }; - - // 计算菜单项位置 - const getMenuItemPosition = (index: number) => { - const positions = [ - { x: 0, y: -80 }, // 上方 - { x: -60, y: -60 }, // 左上 - { x: -80, y: 0 }, // 左方 - { x: -60, y: 60 }, // 左下 - ]; - - const pos = positions[index] || { x: 0, y: 0 }; - let x = menuPosition.x + pos.x; - let y = menuPosition.y + pos.y; - - // // 边界检测 - // const itemSize = 48; - // const margin = 20; - // 边界检测 - const itemSize = 48; - // const margin = 0; - - // x = Math.max( - // // margin + itemSize / 2, - // Math.min(window.innerWidth - margin - itemSize / 2, x) - // ); - // y = Math.max( - // // margin + itemSize / 2, - // Math.min(window.innerHeight - margin - itemSize / 2, y) - // ); - - return { - left: x - itemSize / 2, - top: y - itemSize / 2, - }; - }; - - // 菜单组件 - const MenuComponent = () => ( - <> - {/* 背景遮罩 */} -
setVisible(false)} - /> - - {/* 菜单项 */} - {menuItems.map((item, index) => { - const position = getMenuItemPosition(index); - return ( -
handleItemClick(item)} - // title={item.label} - > - {item.icon} -
- ); - })} - - ); - - return ( - <> - {/* 主按钮 */} -
- -
- {props.value?.icon} -
- {/* {!visible && } */} - 切换语言 -
-
-
-
- - {/* 菜单 - 只在有位置信息时渲染 */} - {visible && - menuPosition.x > 0 && - menuPosition.y > 0 && - createPortal(, document.body)} - - ); -}; - -export default FloatingFanMenu; diff --git a/src/component/petVoiceTranslator copy/index.less b/src/component/petVoiceTranslator copy/index.less deleted file mode 100644 index aa64c44..0000000 --- a/src/component/petVoiceTranslator copy/index.less +++ /dev/null @@ -1,662 +0,0 @@ -// PetVoiceTranslator.less -.pet-voice-translator { - display: flex; - flex-direction: column; - height: 100vh; - background: #ededed; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", - "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, - sans-serif; - position: relative; - overflow: hidden; - - // 处理iOS安全区域 - padding-top: var(--safe-area-inset-top, 0); - padding-bottom: var(--safe-area-inset-bottom, 0); - - // Safari 特殊处理 - -webkit-overflow-scrolling: touch; - -webkit-user-select: none; - -webkit-touch-callout: none; - -webkit-tap-highlight-color: transparent; - - .chat-header { - display: flex; - align-items: center; - padding: 12px 16px; - background: #f7f7f7; - border-bottom: 1px solid #e5e5e5; - flex-shrink: 0; - position: relative; - z-index: 10; - - // 确保头部不被NavBar遮挡 - min-height: 64px; - - .pet-avatar { - width: 40px; - height: 40px; - border-radius: 20px; - background: linear-gradient(45deg, #ff9a9e, #fecfef); - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - margin-right: 12px; - } - - .chat-title { - flex: 1; - - .pet-name { - font-size: 16px; - font-weight: 500; - color: #333; - margin-bottom: 2px; - } - - .chat-subtitle { - font-size: 12px; - color: #999; - } - } - } - - .messages-container { - flex: 1; - overflow-y: auto; - overflow-x: hidden; - padding: 16px 12px; - position: relative; - - // Safari 滚动优化 - -webkit-overflow-scrolling: touch; - -webkit-transform: translateZ(0); - transform: translateZ(0); - - // 确保可以滚动 - overscroll-behavior: contain; - scroll-behavior: smooth; - - // 滚动条样式 - &::-webkit-scrollbar { - width: 0; - background: transparent; - } - - .empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - color: #999; - min-height: 200px; - - .empty-icon { - font-size: 64px; - margin-bottom: 16px; - opacity: 0.6; - } - - .empty-text { - font-size: 16px; - margin-bottom: 8px; - } - - .empty-subtitle { - font-size: 14px; - opacity: 0.8; - } - } - - .message-wrapper { - margin-bottom: 16px; - animation: messageSlideIn 0.3s ease-out; - - -webkit-user-select: none; - user-select: none; - - .message-item { - display: flex; - align-items: flex-start; - - .avatar { - width: 40px; - height: 40px; - border-radius: 6px; - background: linear-gradient(45deg, #ff9a9e, #fecfef); - display: flex; - align-items: center; - justify-content: center; - font-size: 18px; - margin-right: 8px; - flex-shrink: 0; - } - - .message-content { - flex: 1; - max-width: calc(100% - 80px); - - .voice-bubble { - background: #95ec69; - border-radius: 8px; - padding: 8px 12px; - margin-bottom: 6px; - position: relative; - display: inline-block; - min-width: 120px; - cursor: pointer; - user-select: none; - - // 防止长按选择 - -webkit-user-select: none; - -webkit-touch-callout: none; - -webkit-tap-highlight-color: transparent; - - // 优化点击响应 - touch-action: manipulation; - - &::before { - content: ""; - position: absolute; - left: -8px; - top: 12px; - width: 0; - height: 0; - border-top: 6px solid transparent; - border-bottom: 6px solid transparent; - border-right: 8px solid #95ec69; - } - - &:active { - background: #8de354; - transform: scale(0.98); - transition: all 0.1s ease; - } - - .voice-content { - display: flex; - align-items: center; - - .voice-icon { - margin-right: 8px; - font-size: 16px; - color: #333; - - .playing-animation { - display: flex; - align-items: center; - gap: 2px; - - span { - width: 3px; - height: 12px; - background: #333; - border-radius: 1px; - animation: voiceWave 1s infinite ease-in-out; - - &:nth-child(2) { - animation-delay: 0.1s; - } - - &:nth-child(3) { - animation-delay: 0.2s; - } - } - } - } - - .voice-duration { - font-size: 14px; - color: #333; - font-weight: 500; - } - } - } - - .translation-text { - font-size: 13px; - color: #666; - margin-left: 4px; - margin-bottom: 4px; - line-height: 1.4; - word-wrap: break-word; - word-break: break-word; - - &.translating { - .translating-content { - display: flex; - align-items: center; - - .loading-dots { - margin-right: 8px; - - &::after { - content: "..."; - animation: loadingDots 1.5s infinite; - } - } - - .typing-indicator { - display: flex; - gap: 3px; - - span { - width: 4px; - height: 4px; - background: #999; - border-radius: 50%; - animation: typingBounce 1.4s infinite ease-in-out; - - &:nth-child(1) { - animation-delay: 0s; - } - - &:nth-child(2) { - animation-delay: 0.2s; - } - - &:nth-child(3) { - animation-delay: 0.4s; - } - } - } - } - } - - .translation-label { - font-size: 11px; - color: #999; - margin-bottom: 2px; - } - - .translation-content { - color: #333; - word-wrap: break-word; - word-break: break-word; - } - } - - .message-time { - font-size: 11px; - color: #999; - margin-left: 4px; - } - } - } - } - } - - .recording-controls { - background: #f7f7f7; - border-top: 1px solid #e5e5e5; - padding: 12px 16px 20px; - flex-shrink: 0; - position: relative; - z-index: 10; - - // 确保控制区域不被底部安全区域影响 - min-height: 100px; - - // 防止在Safari中被下拉刷新影响 - -webkit-user-select: none; - user-select: none; - - .recorder-wrapper { - display: none; - } - - .control-area { - .recording-info { - text-align: center; - margin-bottom: 12px; - - .recording-indicator { - display: flex; - align-items: center; - justify-content: center; - color: #ff4d4f; - font-size: 14px; - margin-bottom: 4px; - - .recording-dot { - width: 8px; - height: 8px; - background: #ff4d4f; - border-radius: 50%; - margin-right: 8px; - animation: recordingPulse 1s infinite; - } - } - - .recording-tip { - font-size: 12px; - color: #999; - } - } - - .input-area { - display: flex; - flex-direction: column; - align-items: center; - - .button-group { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 8px; - - .cancel-button { - width: 40px; - height: 40px; - border-radius: 20px; - border: none; - background: #ff4d4f; - color: white; - cursor: pointer; - transition: all 0.1s ease; - outline: none; - -webkit-tap-highlight-color: transparent; - -webkit-appearance: none; - -webkit-user-select: none; - display: flex; - align-items: center; - justify-content: center; - animation: slideInLeft 0.3s ease-out; - - // 优化点击响应 - touch-action: manipulation; - - &:active { - transform: scale(0.9); - background: #ff7875; - } - - .cancel-icon { - font-size: 18px; - } - } - - .record-button { - width: 60px; - height: 60px; - border-radius: 30px; - border: none; - background: #1890ff; - color: white; - cursor: pointer; - transition: all 0.1s ease; - outline: none; - -webkit-tap-highlight-color: transparent; - -webkit-appearance: none; - -webkit-user-select: none; - display: flex; - align-items: center; - justify-content: center; - - // 优化点击响应 - touch-action: manipulation; - - &:active { - transform: scale(0.9); - } - - &:disabled { - background: #d9d9d9; - color: #999; - cursor: not-allowed; - } - - &.processing { - background: #faad14; - - cursor: wait; - - .processing-animation { - display: flex; - - align-items: center; - - gap: 3 px; - - span { - width: 3 px; - - height: 16 px; - - background: white; - - border-radius: 1 px; - - animation: processingWave 1.5 s infinite ease - in - out; - - &:nth-child(2) { - animation-delay: 0.2 s; - } - - &:nth-child(3) { - animation-delay: 0.4 s; - } - } - } - } - - &.recording { - background: #ff4d4f; - animation: recordingButtonPulse 1s infinite; - - .recording-animation { - display: flex; - align-items: center; - gap: 3px; - - span { - width: 3px; - height: 16px; - background: white; - border-radius: 1px; - animation: recordingWave 1s infinite ease-in-out; - - &:nth-child(2) { - animation-delay: 0.1s; - } - - &:nth-child(3) { - animation-delay: 0.2s; - } - } - } - } - - .mic-icon { - font-size: 24px; - } - } - } - - .record-hint { - font-size: 12px; - color: #999; - } - } - } - } -} - -// 全局样式,确保页面不会滚动 -html, -body { - margin: 0; - padding: 0; - height: 100%; - overflow: hidden; - -webkit-overflow-scrolling: touch; - -webkit-user-select: none; - -webkit-touch-callout: none; - -webkit-tap-highlight-color: transparent; - - // 防止iOS Safari的橡皮筋效果 - position: fixed; - width: 100%; -} - -// 动画定义 -@keyframes messageSlideIn { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes slideInLeft { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes voiceWave { - 0%, - 40%, - 100% { - transform: scaleY(0.4); - } - 20% { - transform: scaleY(1); - } -} - -@keyframes loadingDots { - 0%, - 20% { - content: "."; - } - 40% { - content: ".."; - } - 60%, - 100% { - content: "..."; - } -} - -@keyframes typingBounce { - 0%, - 60%, - 100% { - transform: translateY(0); - } - 30% { - transform: translateY(-6px); - } -} - -@keyframes recordingPulse { - 0% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.5; - transform: scale(1.2); - } - 100% { - opacity: 1; - transform: scale(1); - } -} - -@keyframes recordingButtonPulse { - 0% { - box-shadow: 0 0 0 0 rgba(255, 77, 79, 0.7); - } - 70% { - box-shadow: 0 0 0 10px rgba(255, 77, 79, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(255, 77, 79, 0); - } -} - -@keyframes recordingWave { - 0%, - 40%, - 100% { - transform: scaleY(0.4); - } - 20% { - transform: scaleY(1); - } -} - -// 移动端适配 -// @media (max-width: 768px) { -// .pet-voice-translator { -// .messages-container { -// padding: 12px 8px; - -// .message-wrapper .message-item .message-content { -// max-width: calc(100% - 60px); -// } -// } - -// .recording-controls { -// padding: 10px 12px 16px; -// } -// } -// } - -// iOS Safari 特殊处理 -@supports (-webkit-touch-callout: none) { - .pet-voice-translator { - // 确保在iOS Safari中正确显示 - // height: 100vh; - height: -webkit-fill-available; - - .messages-container { - -webkit-overflow-scrolling: touch; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - } -} - -// 深色模式适配 -@media (prefers-color-scheme: dark) { - .pet-voice-translator { - background: #1e1e1e; - - .chat-header { - background: #2a2a2a; - border-bottom-color: #3a3a3a; - - .chat-title .pet-name { - color: #fff; - } - } - - .messages-container { - .message-wrapper .message-item .message-content .translation-text { - color: #ccc; - - .translation-content { - color: #fff; - } - } - } - - .recording-controls { - background: #2a2a2a; - border-top-color: #3a3a3a; - } - } -} diff --git a/src/component/petVoiceTranslator copy/index.tsx b/src/component/petVoiceTranslator copy/index.tsx deleted file mode 100644 index 0b99e4a..0000000 --- a/src/component/petVoiceTranslator copy/index.tsx +++ /dev/null @@ -1,916 +0,0 @@ -// PetVoiceTranslator.tsx -import React, { useState, useRef, useEffect, useCallback } from "react"; -import { AudioRecorder, useAudioRecorder } from "react-audio-voice-recorder"; -import { Button, Toast, Dialog } from "antd-mobile"; -import { SoundOutline, CloseOutline } from "antd-mobile-icons"; -import "./index.less"; - -interface Message { - id: string; - type: "voice"; - audioUrl: string; - duration: number; - timestamp: number; - translatedText?: string; - isTranslating?: boolean; - isPlaying?: boolean; -} - -const PetVoiceTranslator: React.FC = () => { - const [messages, setMessages] = useState([]); - const [isRecording, setIsRecording] = useState(false); - const [hasPermission, setHasPermission] = useState(null); - const [currentPlayingId, setCurrentPlayingId] = useState(null); - const [recordingDuration, setRecordingDuration] = useState(0); - const [isInitialized, setIsInitialized] = useState(false); - const [isProcessing, setIsProcessing] = useState(false); - const [permissionChecking, setPermissionChecking] = useState(false); - const [dialogShowing, setDialogShowing] = useState(false); - - const messagesEndRef = useRef(null); - const messagesContainerRef = useRef(null); - const audioRefs = useRef<{ [key: string]: HTMLAudioElement }>({}); - const recordingTimerRef = useRef(); - const isRecordingRef = useRef(false); - const processingTimeoutRef = useRef(); - const permissionCheckTimeoutRef = useRef(); - - // 新增:防止重复初始化的标志 - const initializingRef = useRef(false); - const initializedRef = useRef(false); - - const recorderControls = useAudioRecorder( - { - noiseSuppression: true, - echoCancellation: true, - autoGainControl: true, - }, - (err) => { - console.error("录音错误:", err); - Toast.show("录音失败,请重试"); - resetRecordingState(); - } - ); - - // 使用useEffect的依赖数组为空,确保只执行一次 - useEffect(() => { - console.log("useEffect执行,初始化状态:", { - initializing: initializingRef.current, - initialized: initializedRef.current, - }); - - // 防止重复初始化 - if (initializingRef.current || initializedRef.current) { - console.log("已经初始化或正在初始化,跳过"); - return; - } - - initializeApp(); - - // 清理函数 - return () => { - console.log("组件卸载,执行清理"); - cleanup(); - }; - }, []); // 空依赖数组确保只执行一次 - - useEffect(() => { - isRecordingRef.current = isRecording; - }, [isRecording]); - - // 优化的初始化函数 - const initializeApp = useCallback(async () => { - // 防止重复执行 - if (initializingRef.current || initializedRef.current) { - console.log("初始化已执行或正在执行,跳过重复调用"); - return; - } - - initializingRef.current = true; - console.log("=== 开始初始化应用 ==="); - - try { - console.log("1. 检查麦克风权限..."); - await checkMicrophonePermission(); - - // console.log("2. 设置Safari兼容性..."); - // setupSafariCompatibility(); - - console.log("3. 标记初始化完成..."); - setIsInitialized(true); - initializedRef.current = true; - - console.log("=== 应用初始化完成 ==="); - } catch (error) { - console.error("应用初始化失败:", error); - Toast.show("应用初始化失败"); - // 即使失败也标记为已初始化,避免无限重试 - setIsInitialized(true); - initializedRef.current = true; - } finally { - initializingRef.current = false; - } - }, []); // 空依赖数组,函数只创建一次 - - const cleanup = useCallback(() => { - console.log("执行清理操作..."); - - // 重置初始化标志 - initializingRef.current = false; - initializedRef.current = false; - - if (recordingTimerRef.current) { - clearInterval(recordingTimerRef.current); - } - if (processingTimeoutRef.current) { - clearTimeout(processingTimeoutRef.current); - } - if (permissionCheckTimeoutRef.current) { - clearTimeout(permissionCheckTimeoutRef.current); - } - - Object.values(audioRefs.current).forEach((audio) => { - audio.pause(); - audio.src = ""; - }); - - cleanupSafariCompatibility(); - }, []); - - const resetRecordingState = useCallback(() => { - console.log("重置录音状态"); - setIsRecording(false); - setIsProcessing(false); - setRecordingDuration(0); - // isRecordingRef.current = false; - - if (recordingTimerRef.current) { - clearInterval(recordingTimerRef.current); - recordingTimerRef.current = undefined; - } - - if (processingTimeoutRef.current) { - clearTimeout(processingTimeoutRef.current); - processingTimeoutRef.current = undefined; - } - }, []); - - // 优化的权限检查函数 - const checkMicrophonePermission = useCallback( - async (showToast = false) => { - console.log("检查麦克风权限...", { - permissionChecking, - dialogShowing, - hasPermission, - }); - - // 防止重复检查 - if (permissionChecking) { - console.log("权限检查正在进行中,跳过"); - return hasPermission; - } - - setPermissionChecking(true); - - try { - // 检查浏览器支持 - if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { - console.error("浏览器不支持录音功能"); - setHasPermission(false); - if (showToast) { - Toast.show("浏览器不支持录音功能"); - } - return false; - } - - // 先检查权限状态(如果浏览器支持) - if (navigator.permissions && navigator.permissions.query) { - try { - const permissionStatus = await navigator.permissions.query({ - name: "microphone" as PermissionName, - }); - - // alert(permissionStatus.state); - // console.log("权限状态:", permissionStatus.state); - - if (permissionStatus.state === "denied") { - console.log("权限被拒绝"); - setHasPermission(false); - - if (showToast && !dialogShowing) { - showPermissionDialog(); - } - return false; - } - - if (permissionStatus.state === "granted") { - console.log("权限已授予"); - setHasPermission(true); - return true; - } - } catch (permError) { - console.log("权限查询不支持,继续使用getUserMedia检查"); - } - } - - // 尝试获取媒体流 - const stream = await navigator.mediaDevices.getUserMedia({ - audio: { - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - }); - - console.log("麦克风权限获取成功"); - stream.getTracks().forEach((track) => track.stop()); - setHasPermission(true); - - if (showToast) { - Toast.show("麦克风权限已获取"); - } - return true; - } catch (error) { - console.error("权限检查失败:", error); - setHasPermission(false); - showPermissionDialog(); - return false; - } finally { - setPermissionChecking(false); - } - }, - [permissionChecking, dialogShowing, hasPermission] - ); - - const showPermissionDialog = useCallback(() => { - if (dialogShowing) { - console.log("弹窗已显示,跳过"); - return; - } - - console.log("显示权限弹窗"); - setDialogShowing(true); - - Dialog.confirm({ - title: "需要录音权限", - content: - '为了使用录音翻译功能,需要您授权麦克风权限。请在浏览器设置中允许访问麦克风,然后点击"重新获取权限"。', - confirmText: "重新获取权限", - cancelText: "取消", - onConfirm: async () => { - console.log("用户点击重新获取权限"); - setDialogShowing(false); - - setTimeout(async () => { - await checkMicrophonePermission(true); - }, 500); - }, - onCancel: () => { - console.log("用户取消权限获取"); - setDialogShowing(false); - Toast.show("需要麦克风权限才能使用录音功能"); - }, - onClose: () => { - console.log("弹窗关闭"); - setDialogShowing(false); - }, - }); - }, [dialogShowing, checkMicrophonePermission]); - - const setupSafariCompatibility = useCallback(() => { - console.log("设置Safari兼容性..."); - - const root = document.documentElement; - const isIPhoneX = - /iPhone/.test(navigator.userAgent) && window.screen.height >= 812; - - if (isIPhoneX) { - root.style.setProperty( - "--safe-area-inset-top", - "env(safe-area-inset-top, 44px)" - ); - root.style.setProperty( - "--safe-area-inset-bottom", - "env(safe-area-inset-bottom, 34px)" - ); - } else { - root.style.setProperty( - "--safe-area-inset-top", - "env(safe-area-inset-top, 20px)" - ); - root.style.setProperty( - "--safe-area-inset-bottom", - "env(safe-area-inset-bottom, 0px)" - ); - } - - document.body.style.overflow = "hidden"; - document.body.style.position = "fixed"; - document.body.style.width = "100%"; - document.body.style.height = "100%"; - document.body.style.top = "0"; - document.body.style.left = "0"; - - document.addEventListener("touchstart", preventPullToRefresh, { - passive: false, - }); - document.addEventListener("touchmove", preventPullToRefresh, { - passive: false, - }); - document.addEventListener("touchend", preventDoubleTapZoom, { - passive: false, - }); - - let viewport = document.querySelector("meta[name=viewport]"); - if (!viewport) { - viewport = document.createElement("meta"); - viewport.setAttribute("name", "viewport"); - document.head.appendChild(viewport); - } - viewport.setAttribute( - "content", - "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" - ); - }, []); - - const cleanupSafariCompatibility = useCallback(() => { - console.log("清理Safari兼容性设置..."); - - document.body.style.overflow = ""; - document.body.style.position = ""; - document.body.style.width = ""; - document.body.style.height = ""; - document.body.style.top = ""; - document.body.style.left = ""; - - document.removeEventListener("touchstart", preventPullToRefresh); - document.removeEventListener("touchmove", preventPullToRefresh); - document.removeEventListener("touchend", preventDoubleTapZoom); - }, []); - - const preventPullToRefresh = (e: TouchEvent) => { - const target = e.target as HTMLElement; - const messagesContainer = messagesContainerRef.current; - - if (messagesContainer && messagesContainer.contains(target)) { - return; - } - - if (e.touches.length > 1) { - e.preventDefault(); - return; - } - - const touch = e.touches[0]; - const startY = touch.clientY; - - if (startY < 100 && e.type === "touchstart") { - e.preventDefault(); - } - }; - - let lastTouchEnd = 0; - const preventDoubleTapZoom = (e: TouchEvent) => { - const now = Date.now(); - if (now - lastTouchEnd <= 300) { - e.preventDefault(); - } - lastTouchEnd = now; - }; - - const stopAllAudio = () => { - if (currentPlayingId && audioRefs.current[currentPlayingId]) { - audioRefs.current[currentPlayingId].pause(); - audioRefs.current[currentPlayingId].currentTime = 0; - - setMessages((prev) => - prev.map((msg) => - msg.id === currentPlayingId ? { ...msg, isPlaying: false } : msg - ) - ); - setCurrentPlayingId(null); - } - - Object.values(audioRefs.current).forEach((audio) => { - if (!audio.paused) { - audio.pause(); - audio.currentTime = 0; - } - }); - }; - - const startRecording = useCallback(async () => { - console.log("=== 开始录音函数调用 ==="); - - if (isProcessing || isRecordingRef.current) { - console.log("正在处理中或已在录音,忽略此次调用"); - return; - } - - if (!isInitialized || !initializedRef.current) { - console.log("应用未初始化完成"); - Toast.show("应用正在初始化,请稍后重试"); - return; - } - - if (!hasPermission) { - console.log("没有录音权限,尝试获取权限"); - const granted = await checkMicrophonePermission(true); - if (!granted) { - console.log("权限获取失败"); - return; - } - } - - try { - setIsProcessing(true); - setIsRecording(true); - setRecordingDuration(0); - - stopAllAudio(); - - await recorderControls.startRecording(); - - recordingTimerRef.current = setInterval(() => { - setRecordingDuration((prev) => prev + 1); - }, 1000); - - Toast.show("开始录音..."); - - processingTimeoutRef.current = setTimeout(() => { - setIsProcessing(false); - }, 1000); - } catch (error) { - console.error("开始录音失败:", error); - resetRecordingState(); - - if ( - error.name === "NotAllowedError" || - error.name === "PermissionDeniedError" - ) { - setHasPermission(false); - if (!dialogShowing) { - showPermissionDialog(); - } - } else { - Toast.show(`录音失败: ${error.message || "未知错误"}`); - } - } - }, [ - isProcessing, - hasPermission, - isInitialized, - recorderControls, - checkMicrophonePermission, - dialogShowing, - showPermissionDialog, - ]); - - const stopRecording = useCallback(() => { - console.log("=== 停止录音函数调用 ==="); - - if (isProcessing || !isRecordingRef.current) { - console.log("正在处理中或未在录音,忽略此次调用"); - return; - } - - try { - setIsProcessing(true); - setIsRecording(false); - - if (recordingTimerRef.current) { - clearInterval(recordingTimerRef.current); - recordingTimerRef.current = undefined; - } - - recorderControls.stopRecording(); - - processingTimeoutRef.current = setTimeout(() => { - setIsProcessing(false); - }, 2000); - } catch (error) { - console.error("停止录音失败:", error); - resetRecordingState(); - Toast.show("停止录音失败"); - } - }, [isProcessing, recorderControls]); - - const onRecordingComplete = useCallback( - (blob: Blob) => { - console.log("=== 录音完成回调 ==="); - - setIsProcessing(false); - if (processingTimeoutRef.current) { - clearTimeout(processingTimeoutRef.current); - } - - if (recordingDuration < 1) { - Toast.show("录音时间太短,请重新录音"); - setRecordingDuration(0); - return; - } - - const audioUrl = URL.createObjectURL(blob); - const newMessage: Message = { - id: Date.now().toString(), - type: "voice", - audioUrl, - duration: recordingDuration, - timestamp: Date.now(), - isTranslating: true, - }; - - setMessages((prev) => [...prev, newMessage]); - setRecordingDuration(0); - - setTimeout(() => { - translateAudio(newMessage.id, blob); - }, 1000); - - Toast.show("语音已发送"); - }, - [recordingDuration] - ); - - const cancelRecording = useCallback(() => { - console.log("=== 取消录音 ==="); - - try { - if (isRecordingRef.current) { - recorderControls.stopRecording(); - } - } catch (error) { - console.error("取消录音失败:", error); - } - - resetRecordingState(); - Toast.show("已取消录音"); - }, [recorderControls, resetRecordingState]); - - const handleRecordButtonClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - - console.log("=== 录音按钮点击 ==="); - - if (isProcessing || permissionChecking) { - console.log("正在处理中,忽略点击"); - return; - } - - if (isRecordingRef.current) { - stopRecording(); - } else { - startRecording(); - } - }, - [isProcessing, permissionChecking, startRecording, stopRecording] - ); - - const translateAudio = async (messageId: string, audioBlob: Blob) => { - try { - const translatedText = await mockTranslateAudio(audioBlob); - - setMessages((prev) => - prev.map((msg) => - msg.id === messageId - ? { ...msg, translatedText, isTranslating: false } - : msg - ) - ); - } catch (error) { - console.error("翻译失败:", error); - Toast.show("翻译失败,请重试"); - - setMessages((prev) => - prev.map((msg) => - msg.id === messageId - ? { - ...msg, - isTranslating: false, - translatedText: "翻译失败,请重试", - } - : msg - ) - ); - } - }; - - const mockTranslateAudio = async (audioBlob: Blob): Promise => { - return new Promise((resolve) => { - setTimeout(() => { - const mockTranslations = [ - "汪汪汪!我饿了,想吃东西!🍖", - "喵喵~我想要抱抱!🤗", - "我想出去玩耍!🎾", - "我很开心!😊", - "我有点害怕...😰", - "我想睡觉了~😴", - "主人,陪我玩一会儿吧!🎮", - "我想喝水了💧", - "外面有什么声音?👂", - "我爱你,主人!❤️", - ]; - const randomTranslation = - mockTranslations[Math.floor(Math.random() * mockTranslations.length)]; - resolve(randomTranslation); - }, 2000 + Math.random() * 2000); - }); - }; - - const playAudio = (messageId: string, audioUrl: string) => { - if (isRecording) { - Toast.show("录音中,无法播放音频"); - return; - } - - if (currentPlayingId === messageId) { - if (audioRefs.current[messageId]) { - audioRefs.current[messageId].pause(); - audioRefs.current[messageId].currentTime = 0; - } - setCurrentPlayingId(null); - setMessages((prev) => - prev.map((msg) => - msg.id === messageId ? { ...msg, isPlaying: false } : msg - ) - ); - return; - } - - stopAllAudio(); - - if (!audioRefs.current[messageId]) { - audioRefs.current[messageId] = new Audio(audioUrl); - } - - const audio = audioRefs.current[messageId]; - audio.currentTime = 0; - - audio.onended = () => { - setCurrentPlayingId(null); - setMessages((prev) => - prev.map((msg) => - msg.id === messageId ? { ...msg, isPlaying: false } : msg - ) - ); - }; - - audio.onerror = (error) => { - console.error("音频播放错误:", error); - Toast.show("音频播放失败"); - setCurrentPlayingId(null); - setMessages((prev) => - prev.map((msg) => - msg.id === messageId ? { ...msg, isPlaying: false } : msg - ) - ); - }; - - audio - .play() - .then(() => { - setCurrentPlayingId(messageId); - setMessages((prev) => - prev.map((msg) => - msg.id === messageId - ? { ...msg, isPlaying: true } - : { ...msg, isPlaying: false } - ) - ); - }) - .catch((error) => { - console.error("音频播放失败:", error); - Toast.show("音频播放失败"); - }); - }; - - const formatDuration = (seconds: number) => { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, "0")}`; - }; - - const formatTime = (timestamp: number) => { - return new Date(timestamp).toLocaleTimeString("zh-CN", { - hour: "2-digit", - minute: "2-digit", - }); - }; - - return ( -
- {/* 调试信息 */} - {process.env.NODE_ENV === "development" && ( -
-
初始化中: {initializingRef.current ? "⏳" : "✅"}
-
已初始化: {initializedRef.current ? "✅" : "❌"}
-
状态初始化: {isInitialized ? "✅" : "❌"}
-
- 权限: {hasPermission === null ? "⏳" : hasPermission ? "✅" : "❌"} -
-
录音: {isRecording ? "🔴" : "⚪"}
-
处理中: {isProcessing ? "⏳" : "✅"}
-
权限检查: {permissionChecking ? "⏳" : "✅"}
-
弹窗: {dialogShowing ? "📱" : "❌"}
-
- )} - - {/* 头部 */} -
-
🐾
-
-
我的宠物
-
- {initializingRef.current - ? "初始化中..." - : !isInitialized - ? "等待初始化..." - : permissionChecking - ? "检查权限中..." - : hasPermission === null - ? "权限状态未知" - : hasPermission - ? "宠物语音翻译器" - : "需要麦克风权限"} -
-
-
- - {/* 消息列表 */} -
- {messages.length === 0 ? ( -
-
🎤
-
- {initializingRef.current - ? "正在初始化..." - : !isInitialized - ? "等待初始化..." - : permissionChecking - ? "正在检查权限..." - : hasPermission === null - ? "权限状态未知" - : !hasPermission - ? "请授权麦克风权限" - : "点击下方按钮开始录音"} -
-
听听你的宠物想说什么~
-
- ) : ( - messages.map((message) => ( -
-
-
🐾
-
-
-
playAudio(message.id, message.audioUrl)} - > -
- {message.isPlaying ? ( -
- - - -
- ) : ( - - )} -
-
- {formatDuration(message.duration)} -
-
-
- - {message.isTranslating ? ( -
-
- 翻译中 -
- - - -
-
-
- ) : message.translatedText ? ( -
-
🐾 宠物说:
-
- {message.translatedText} -
-
- ) : null} - -
- {formatTime(message.timestamp)} -
-
-
-
- )) - )} -
-
- - {/* 录音控制区域 */} -
-
- -
- -
- {isRecording && ( -
-
-
- 录音中 {formatDuration(recordingDuration)} -
-
再次点击完成录音并发送
-
- )} - -
-
- {/* {isRecording && ( - - )} */} - - -
- - {!isRecording && ( -
- {initializingRef.current - ? "初始化中..." - : !isInitialized - ? "等待初始化..." - : permissionChecking - ? "检查权限中..." - : !hasPermission - ? "需要麦克风权限" - : isProcessing - ? "处理中..." - : "点击录音"} -
- )} -
-
-
-
- ); -}; - -export default PetVoiceTranslator; diff --git a/src/component/petVoiceTranslator/index.less b/src/component/petVoiceTranslator/index.less deleted file mode 100644 index a9ce8d4..0000000 --- a/src/component/petVoiceTranslator/index.less +++ /dev/null @@ -1,660 +0,0 @@ -// PetVoiceTranslator.less -.pet-voice-translator { - display: flex; - flex-direction: column; - height: 100%; - background: #ededed; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", - "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, - sans-serif; - position: relative; - overflow: hidden; - // 处理iOS安全区域 - padding-bottom: env(safe-area-inset-bottom); - - // Safari 特殊处理 - -webkit-overflow-scrolling: touch; - -webkit-user-select: none; - -webkit-touch-callout: none; - -webkit-tap-highlight-color: transparent; - - .chat-header { - display: flex; - align-items: center; - padding: 12px 16px; - background: #f7f7f7; - border-bottom: 1px solid #e5e5e5; - flex-shrink: 0; - position: relative; - z-index: 10; - - // 确保头部不被NavBar遮挡 - min-height: 64px; - - .pet-avatar { - width: 40px; - height: 40px; - border-radius: 20px; - background: linear-gradient(45deg, #ff9a9e, #fecfef); - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - margin-right: 12px; - } - - .chat-title { - flex: 1; - - .pet-name { - font-size: 16px; - font-weight: 500; - color: #333; - margin-bottom: 2px; - } - - .chat-subtitle { - font-size: 12px; - color: #999; - } - } - } - - .messages-container { - flex: 1; - overflow-y: auto; - overflow-x: hidden; - padding: 16px 12px; - position: relative; - - // Safari 滚动优化 - -webkit-overflow-scrolling: touch; - -webkit-transform: translateZ(0); - transform: translateZ(0); - - // 确保可以滚动 - overscroll-behavior: contain; - scroll-behavior: smooth; - - // 滚动条样式 - &::-webkit-scrollbar { - width: 0; - background: transparent; - } - - .empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - color: #999; - min-height: 200px; - - .empty-icon { - font-size: 64px; - margin-bottom: 16px; - opacity: 0.6; - } - - .empty-text { - font-size: 16px; - margin-bottom: 8px; - } - - .empty-subtitle { - font-size: 14px; - opacity: 0.8; - } - } - - .message-wrapper { - margin-bottom: 16px; - animation: messageSlideIn 0.3s ease-out; - - -webkit-user-select: none; - user-select: none; - - .message-item { - display: flex; - align-items: flex-start; - - .avatar { - width: 40px; - height: 40px; - border-radius: 6px; - background: linear-gradient(45deg, #ff9a9e, #fecfef); - display: flex; - align-items: center; - justify-content: center; - font-size: 18px; - margin-right: 8px; - flex-shrink: 0; - } - - .message-content { - flex: 1; - max-width: calc(100% - 80px); - - .voice-bubble { - background: #95ec69; - border-radius: 8px; - padding: 8px 12px; - margin-bottom: 6px; - position: relative; - display: inline-block; - min-width: 120px; - cursor: pointer; - user-select: none; - - // 防止长按选择 - -webkit-user-select: none; - -webkit-touch-callout: none; - -webkit-tap-highlight-color: transparent; - - // 优化点击响应 - touch-action: manipulation; - - &::before { - content: ""; - position: absolute; - left: -8px; - top: 12px; - width: 0; - height: 0; - border-top: 6px solid transparent; - border-bottom: 6px solid transparent; - border-right: 8px solid #95ec69; - } - - &:active { - background: #8de354; - transform: scale(0.98); - transition: all 0.1s ease; - } - - .voice-content { - display: flex; - align-items: center; - - .voice-icon { - margin-right: 8px; - font-size: 16px; - color: #333; - - .playing-animation { - display: flex; - align-items: center; - gap: 2px; - - span { - width: 3px; - height: 12px; - background: #333; - border-radius: 1px; - animation: voiceWave 1s infinite ease-in-out; - - &:nth-child(2) { - animation-delay: 0.1s; - } - - &:nth-child(3) { - animation-delay: 0.2s; - } - } - } - } - - .voice-duration { - font-size: 14px; - color: #333; - font-weight: 500; - } - } - } - - .translation-text { - font-size: 13px; - color: #666; - margin-left: 4px; - margin-bottom: 4px; - line-height: 1.4; - word-wrap: break-word; - word-break: break-word; - - &.translating { - .translating-content { - display: flex; - align-items: center; - - .loading-dots { - margin-right: 8px; - - &::after { - content: "..."; - animation: loadingDots 1.5s infinite; - } - } - - .typing-indicator { - display: flex; - gap: 3px; - - span { - width: 4px; - height: 4px; - background: #999; - border-radius: 50%; - animation: typingBounce 1.4s infinite ease-in-out; - - &:nth-child(1) { - animation-delay: 0s; - } - - &:nth-child(2) { - animation-delay: 0.2s; - } - - &:nth-child(3) { - animation-delay: 0.4s; - } - } - } - } - } - - .translation-label { - font-size: 11px; - color: #999; - margin-bottom: 2px; - } - - .translation-content { - color: #333; - word-wrap: break-word; - word-break: break-word; - } - } - - .message-time { - font-size: 11px; - color: #999; - margin-left: 4px; - } - } - } - } - } - - .recording-controls { - background: #f7f7f7; - border-top: 1px solid #e5e5e5; - padding: 12px 16px 20px; - flex-shrink: 0; - position: relative; - z-index: 10; - - // 确保控制区域不被底部安全区域影响 - min-height: 100px; - - // 防止在Safari中被下拉刷新影响 - -webkit-user-select: none; - user-select: none; - - .recorder-wrapper { - display: none; - } - - .control-area { - .recording-info { - text-align: center; - margin-bottom: 12px; - - .recording-indicator { - display: flex; - align-items: center; - justify-content: center; - color: #ff4d4f; - font-size: 14px; - margin-bottom: 4px; - - .recording-dot { - width: 8px; - height: 8px; - background: #ff4d4f; - border-radius: 50%; - margin-right: 8px; - animation: recordingPulse 1s infinite; - } - } - - .recording-tip { - font-size: 12px; - color: #999; - } - } - - .input-area { - display: flex; - flex-direction: column; - align-items: center; - - .button-group { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 8px; - - .cancel-button { - width: 40px; - height: 40px; - border-radius: 20px; - border: none; - background: #ff4d4f; - color: white; - cursor: pointer; - transition: all 0.1s ease; - outline: none; - -webkit-tap-highlight-color: transparent; - -webkit-appearance: none; - -webkit-user-select: none; - display: flex; - align-items: center; - justify-content: center; - animation: slideInLeft 0.3s ease-out; - - // 优化点击响应 - touch-action: manipulation; - - &:active { - transform: scale(0.9); - background: #ff7875; - } - - .cancel-icon { - font-size: 18px; - } - } - - .record-button { - width: 60px; - height: 60px; - border-radius: 30px; - border: none; - background: #1890ff; - color: white; - cursor: pointer; - transition: all 0.1s ease; - outline: none; - -webkit-tap-highlight-color: transparent; - -webkit-appearance: none; - -webkit-user-select: none; - display: flex; - align-items: center; - justify-content: center; - - // 优化点击响应 - touch-action: manipulation; - - &:active { - transform: scale(0.9); - } - - &:disabled { - background: #d9d9d9; - color: #999; - cursor: not-allowed; - } - - &.processing { - background: #faad14; - - cursor: wait; - - .processing-animation { - display: flex; - - align-items: center; - - gap: 3 px; - - span { - width: 3 px; - - height: 16 px; - - background: white; - - border-radius: 1 px; - - animation: processingWave 1.5 s infinite ease - in - out; - - &:nth-child(2) { - animation-delay: 0.2 s; - } - - &:nth-child(3) { - animation-delay: 0.4 s; - } - } - } - } - - &.recording { - background: #ff4d4f; - animation: recordingButtonPulse 1s infinite; - - .recording-animation { - display: flex; - align-items: center; - gap: 3px; - - span { - width: 3px; - height: 16px; - background: white; - border-radius: 1px; - animation: recordingWave 1s infinite ease-in-out; - - &:nth-child(2) { - animation-delay: 0.1s; - } - - &:nth-child(3) { - animation-delay: 0.2s; - } - } - } - } - - .mic-icon { - font-size: 24px; - } - } - } - - .record-hint { - font-size: 12px; - color: #999; - } - } - } - } -} - -// 全局样式,确保页面不会滚动 -html, -body { - margin: 0; - padding: 0; - height: 100%; - overflow: hidden; - -webkit-overflow-scrolling: touch; - -webkit-user-select: none; - -webkit-touch-callout: none; - -webkit-tap-highlight-color: transparent; - - // 防止iOS Safari的橡皮筋效果 - position: fixed; - width: 100%; -} - -// 动画定义 -@keyframes messageSlideIn { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes slideInLeft { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes voiceWave { - 0%, - 40%, - 100% { - transform: scaleY(0.4); - } - 20% { - transform: scaleY(1); - } -} - -@keyframes loadingDots { - 0%, - 20% { - content: "."; - } - 40% { - content: ".."; - } - 60%, - 100% { - content: "..."; - } -} - -@keyframes typingBounce { - 0%, - 60%, - 100% { - transform: translateY(0); - } - 30% { - transform: translateY(-6px); - } -} - -@keyframes recordingPulse { - 0% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.5; - transform: scale(1.2); - } - 100% { - opacity: 1; - transform: scale(1); - } -} - -@keyframes recordingButtonPulse { - 0% { - box-shadow: 0 0 0 0 rgba(255, 77, 79, 0.7); - } - 70% { - box-shadow: 0 0 0 10px rgba(255, 77, 79, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(255, 77, 79, 0); - } -} - -@keyframes recordingWave { - 0%, - 40%, - 100% { - transform: scaleY(0.4); - } - 20% { - transform: scaleY(1); - } -} - -// 移动端适配 -// @media (max-width: 768px) { -// .pet-voice-translator { -// .messages-container { -// padding: 12px 8px; - -// .message-wrapper .message-item .message-content { -// max-width: calc(100% - 60px); -// } -// } - -// .recording-controls { -// padding: 10px 12px 16px; -// } -// } -// } - -// iOS Safari 特殊处理 -@supports (-webkit-touch-callout: none) { - .pet-voice-translator { - // 确保在iOS Safari中正确显示 - // height: 100vh; - height: -webkit-fill-available; - - .messages-container { - -webkit-overflow-scrolling: touch; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - } -} - -// 深色模式适配 -@media (prefers-color-scheme: dark) { - .pet-voice-translator { - background: #1e1e1e; - - .chat-header { - background: #2a2a2a; - border-bottom-color: #3a3a3a; - - .chat-title .pet-name { - color: #fff; - } - } - - .messages-container { - .message-wrapper .message-item .message-content .translation-text { - color: #ccc; - - .translation-content { - color: #fff; - } - } - } - - .recording-controls { - background: #2a2a2a; - border-top-color: #3a3a3a; - } - } -} diff --git a/src/component/petVoiceTranslator/index.tsx b/src/component/petVoiceTranslator/index.tsx deleted file mode 100644 index a0a4949..0000000 --- a/src/component/petVoiceTranslator/index.tsx +++ /dev/null @@ -1,1104 +0,0 @@ -// // PetVoiceTranslator.tsx -// import React, { useState, useRef, useEffect, useCallback } from "react"; -// import { AudioRecorder, useAudioRecorder } from "react-audio-voice-recorder"; -// import { Button, Toast, Dialog } from "antd-mobile"; -// import { SoundOutline, CloseOutline } from "antd-mobile-icons"; -// import "./index.less"; - -// interface Message { -// id: string; -// type: "voice"; -// audioUrl: string; -// duration: number; -// timestamp: number; -// translatedText?: string; -// isTranslating?: boolean; -// isPlaying?: boolean; -// } - -// const PetVoiceTranslator: React.FC = () => { -// const [messages, setMessages] = useState([]); -// const [isRecording, setIsRecording] = useState(false); -// const [hasPermission, setHasPermission] = useState(null); -// const [currentPlayingId, setCurrentPlayingId] = useState(null); -// const [recordingDuration, setRecordingDuration] = useState(0); -// const [isInitialized, setIsInitialized] = useState(false); -// const [permissionChecking, setPermissionChecking] = useState(false); -// const [dialogShowing, setDialogShowing] = useState(false); - -// const messagesEndRef = useRef(null); -// const messagesContainerRef = useRef(null); -// const audioRefs = useRef<{ [key: string]: HTMLAudioElement }>({}); -// const recordingTimerRef = useRef(); -// const isRecordingRef = useRef(false); -// const initializingRef = useRef(false); -// const initializedRef = useRef(false); -// const isCancelledRef = useRef(false); -// const recordingStartTimeRef = useRef(0); - -// // 音效相关 -// const sendSoundRef = useRef(null); -// const startRecordSoundRef = useRef(null); - -// const recorderControls = useAudioRecorder( -// { -// noiseSuppression: true, -// echoCancellation: true, -// autoGainControl: true, -// }, -// (err) => { -// console.error("录音错误:", err); -// Toast.show("录音失败,请重试"); -// resetRecordingState(); -// } -// ); - -// useEffect(() => { -// if (initializingRef.current || initializedRef.current) { -// return; -// } -// initializeApp(); - -// return () => { -// cleanup(); -// }; -// }, []); - -// useEffect(() => { -// scrollToBottom(); -// }, [messages]); - -// useEffect(() => { -// isRecordingRef.current = isRecording; -// }, [isRecording]); - -// // 初始化音效 -// useEffect(() => { -// initializeSounds(); -// return () => { -// cleanupSounds(); -// }; -// }, []); - -// const initializeSounds = () => { -// try { -// // 发送音效 - 使用Web Audio API生成 -// sendSoundRef.current = createSendSound(); - -// // 开始录音音效 -// startRecordSoundRef.current = createStartRecordSound(); - -// console.log("音效初始化完成"); -// } catch (error) { -// console.error("音效初始化失败:", error); -// } -// }; - -// const cleanupSounds = () => { -// if (sendSoundRef.current) { -// sendSoundRef.current.pause(); -// sendSoundRef.current = null; -// } -// if (startRecordSoundRef.current) { -// startRecordSoundRef.current.pause(); -// startRecordSoundRef.current = null; -// } -// }; - -// // 创建发送音效 - 清脆的"叮"声 -// const createSendSound = () => { -// try { -// const audioContext = new (window.AudioContext || -// (window as any).webkitAudioContext)(); -// const oscillator = audioContext.createOscillator(); -// const gainNode = audioContext.createGain(); - -// oscillator.connect(gainNode); -// gainNode.connect(audioContext.destination); - -// // 设置音调 - 清脆的高音 -// oscillator.frequency.setValueAtTime(800, audioContext.currentTime); -// oscillator.frequency.exponentialRampToValueAtTime( -// 1200, -// audioContext.currentTime + 0.1 -// ); - -// // 设置音量包络 -// gainNode.gain.setValueAtTime(0, audioContext.currentTime); -// gainNode.gain.linearRampToValueAtTime( -// 0.3, -// audioContext.currentTime + 0.01 -// ); -// gainNode.gain.exponentialRampToValueAtTime( -// 0.01, -// audioContext.currentTime + 0.3 -// ); - -// oscillator.type = "sine"; - -// // 创建音频元素 -// const audio = new Audio(); - -// // 重写play方法来播放合成音效 -// audio.play = () => { -// return new Promise((resolve) => { -// try { -// const newOscillator = audioContext.createOscillator(); -// const newGainNode = audioContext.createGain(); - -// newOscillator.connect(newGainNode); -// newGainNode.connect(audioContext.destination); - -// newOscillator.frequency.setValueAtTime( -// 800, -// audioContext.currentTime -// ); -// newOscillator.frequency.exponentialRampToValueAtTime( -// 1200, -// audioContext.currentTime + 0.1 -// ); - -// newGainNode.gain.setValueAtTime(0, audioContext.currentTime); -// newGainNode.gain.linearRampToValueAtTime( -// 0.3, -// audioContext.currentTime + 0.01 -// ); -// newGainNode.gain.exponentialRampToValueAtTime( -// 0.01, -// audioContext.currentTime + 0.3 -// ); - -// newOscillator.type = "sine"; -// newOscillator.start(audioContext.currentTime); -// newOscillator.stop(audioContext.currentTime + 0.3); - -// setTimeout(() => resolve(undefined), 300); -// } catch (error) { -// console.error("播放发送音效失败:", error); -// resolve(undefined); -// } -// }); -// }; - -// return audio; -// } catch (error) { -// console.error("创建发送音效失败:", error); -// return new Audio(); // 返回空音频对象 -// } -// }; - -// // 创建开始录音音效 - 低沉的"嘟"声 -// const createStartRecordSound = () => { -// try { -// const audio = new Audio(); - -// audio.play = () => { -// return new Promise((resolve) => { -// try { -// const audioContext = new (window.AudioContext || -// (window as any).webkitAudioContext)(); -// const oscillator = audioContext.createOscillator(); -// const gainNode = audioContext.createGain(); - -// oscillator.connect(gainNode); -// gainNode.connect(audioContext.destination); - -// // 设置音调 - 低沉的音 -// oscillator.frequency.setValueAtTime(400, audioContext.currentTime); - -// // 设置音量包络 -// gainNode.gain.setValueAtTime(0, audioContext.currentTime); -// gainNode.gain.linearRampToValueAtTime( -// 0.2, -// audioContext.currentTime + 0.05 -// ); -// gainNode.gain.linearRampToValueAtTime( -// 0, -// audioContext.currentTime + 0.2 -// ); - -// oscillator.type = "sine"; -// oscillator.start(audioContext.currentTime); -// oscillator.stop(audioContext.currentTime + 0.2); - -// setTimeout(() => resolve(undefined), 200); -// } catch (error) { -// console.error("播放录音音效失败:", error); -// resolve(undefined); -// } -// }); -// }; - -// return audio; -// } catch (error) { -// console.error("创建录音音效失败:", error); -// return new Audio(); -// } -// }; - -// // 震动函数 -// const vibrate = (pattern: number | number[]) => { -// try { -// if ("vibrate" in navigator) { -// navigator.vibrate(pattern); -// console.log("震动:", pattern); -// } else { -// console.log("设备不支持震动"); -// } -// } catch (error) { -// console.error("震动失败:", error); -// } -// }; - -// // 播放音效 -// const playSound = async (soundRef: React.RefObject) => { -// try { -// if (soundRef.current) { -// await soundRef.current.play(); -// } -// } catch (error) { -// console.error("播放音效失败:", error); -// } -// }; - -// const initializeApp = useCallback(async () => { -// if (initializingRef.current || initializedRef.current) { -// return; -// } - -// initializingRef.current = true; -// console.log("=== 开始初始化应用 ==="); - -// try { -// await checkMicrophonePermission(); -// setupSafariCompatibility(); -// setIsInitialized(true); -// initializedRef.current = true; -// console.log("=== 应用初始化完成 ==="); -// } catch (error) { -// console.error("应用初始化失败:", error); -// Toast.show("应用初始化失败"); -// setIsInitialized(true); -// initializedRef.current = true; -// } finally { -// initializingRef.current = false; -// } -// }, []); - -// const cleanup = useCallback(() => { -// console.log("执行清理操作..."); - -// initializingRef.current = false; -// initializedRef.current = false; -// isCancelledRef.current = false; -// recordingStartTimeRef.current = 0; - -// if (recordingTimerRef.current) { -// clearInterval(recordingTimerRef.current); -// } - -// Object.values(audioRefs.current).forEach((audio) => { -// audio.pause(); -// audio.src = ""; -// }); - -// cleanupSounds(); -// cleanupSafariCompatibility(); -// }, []); - -// const resetRecordingState = useCallback(() => { -// console.log("重置录音状态"); -// setIsRecording(false); -// setRecordingDuration(0); -// isRecordingRef.current = false; -// isCancelledRef.current = false; -// recordingStartTimeRef.current = 0; - -// if (recordingTimerRef.current) { -// clearInterval(recordingTimerRef.current); -// recordingTimerRef.current = undefined; -// } -// }, []); - -// const checkMicrophonePermission = useCallback( -// async (showToast = false) => { -// if (permissionChecking) { -// return hasPermission; -// } - -// setPermissionChecking(true); - -// try { -// if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { -// setHasPermission(false); -// if (showToast) { -// Toast.show("浏览器不支持录音功能"); -// } -// return false; -// } - -// if (navigator.permissions && navigator.permissions.query) { -// try { -// const permissionStatus = await navigator.permissions.query({ -// name: "microphone" as PermissionName, -// }); - -// if (permissionStatus.state === "denied") { -// setHasPermission(false); -// if (showToast && !dialogShowing) { -// showPermissionDialog(); -// } -// return false; -// } - -// if (permissionStatus.state === "granted") { -// setHasPermission(true); -// return true; -// } -// } catch (permError) { -// console.log("权限查询不支持,继续使用getUserMedia检查"); -// } -// } - -// const stream = await navigator.mediaDevices.getUserMedia({ -// audio: { -// echoCancellation: true, -// noiseSuppression: true, -// autoGainControl: true, -// }, -// }); - -// stream.getTracks().forEach((track) => track.stop()); -// setHasPermission(true); - -// if (showToast) { -// Toast.show("麦克风权限已获取"); -// } -// return true; -// } catch (error) { -// console.error("权限检查失败:", error); -// setHasPermission(false); - -// if (showToast && !dialogShowing) { -// showPermissionDialog(); -// } -// return false; -// } finally { -// setPermissionChecking(false); -// } -// }, -// [permissionChecking, dialogShowing, hasPermission] -// ); - -// const showPermissionDialog = useCallback(() => { -// if (dialogShowing) { -// return; -// } - -// setDialogShowing(true); - -// Dialog.confirm({ -// title: "需要录音权限", -// content: -// '为了使用录音翻译功能,需要您授权麦克风权限。请在浏览器设置中允许访问麦克风,然后点击"重新获取权限"。', -// confirmText: "重新获取权限", -// cancelText: "取消", -// onConfirm: async () => { -// setDialogShowing(false); -// setTimeout(async () => { -// await checkMicrophonePermission(true); -// }, 500); -// }, -// onCancel: () => { -// setDialogShowing(false); -// Toast.show("需要麦克风权限才能使用录音功能"); -// }, -// onClose: () => { -// setDialogShowing(false); -// }, -// }); -// }, [dialogShowing, checkMicrophonePermission]); - -// const setupSafariCompatibility = useCallback(() => { -// const root = document.documentElement; -// const isIPhoneX = -// /iPhone/.test(navigator.userAgent) && window.screen.height >= 812; - -// if (isIPhoneX) { -// root.style.setProperty( -// "--safe-area-inset-top", -// "env(safe-area-inset-top, 44px)" -// ); -// root.style.setProperty( -// "--safe-area-inset-bottom", -// "env(safe-area-inset-bottom, 34px)" -// ); -// } else { -// root.style.setProperty( -// "--safe-area-inset-top", -// "env(safe-area-inset-top, 20px)" -// ); -// root.style.setProperty( -// "--safe-area-inset-bottom", -// "env(safe-area-inset-bottom, 0px)" -// ); -// } - -// document.body.style.overflow = "hidden"; -// document.body.style.position = "fixed"; -// document.body.style.width = "100%"; -// document.body.style.height = "100%"; -// document.body.style.top = "0"; -// document.body.style.left = "0"; - -// document.addEventListener("touchstart", preventPullToRefresh, { -// passive: false, -// }); -// document.addEventListener("touchmove", preventPullToRefresh, { -// passive: false, -// }); -// document.addEventListener("touchend", preventDoubleTapZoom, { -// passive: false, -// }); - -// let viewport = document.querySelector("meta[name=viewport]"); -// if (!viewport) { -// viewport = document.createElement("meta"); -// viewport.setAttribute("name", "viewport"); -// document.head.appendChild(viewport); -// } -// viewport.setAttribute( -// "content", -// "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" -// ); -// }, []); - -// const cleanupSafariCompatibility = useCallback(() => { -// document.body.style.overflow = ""; -// document.body.style.position = ""; -// document.body.style.width = ""; -// document.body.style.height = ""; -// document.body.style.top = ""; -// document.body.style.left = ""; - -// document.removeEventListener("touchstart", preventPullToRefresh); -// document.removeEventListener("touchmove", preventPullToRefresh); -// document.removeEventListener("touchend", preventDoubleTapZoom); -// }, []); - -// const preventPullToRefresh = (e: TouchEvent) => { -// const target = e.target as HTMLElement; -// const messagesContainer = messagesContainerRef.current; - -// if (messagesContainer && messagesContainer.contains(target)) { -// return; -// } - -// if (e.touches.length > 1) { -// e.preventDefault(); -// return; -// } - -// const touch = e.touches[0]; -// const startY = touch.clientY; - -// if (startY < 100 && e.type === "touchstart") { -// e.preventDefault(); -// } -// }; - -// let lastTouchEnd = 0; -// const preventDoubleTapZoom = (e: TouchEvent) => { -// const now = Date.now(); -// if (now - lastTouchEnd <= 300) { -// e.preventDefault(); -// } -// lastTouchEnd = now; -// }; - -// const scrollToBottom = () => { -// if (messagesContainerRef.current) { -// const container = messagesContainerRef.current; -// requestAnimationFrame(() => { -// container.scrollTop = container.scrollHeight; -// }); -// } -// }; - -// const stopAllAudio = () => { -// if (currentPlayingId && audioRefs.current[currentPlayingId]) { -// audioRefs.current[currentPlayingId].pause(); -// audioRefs.current[currentPlayingId].currentTime = 0; - -// setMessages((prev) => -// prev.map((msg) => -// msg.id === currentPlayingId ? { ...msg, isPlaying: false } : msg -// ) -// ); -// setCurrentPlayingId(null); -// } - -// Object.values(audioRefs.current).forEach((audio) => { -// if (!audio.paused) { -// audio.pause(); -// audio.currentTime = 0; -// } -// }); -// }; - -// const startRecording = useCallback(async () => { -// console.log("=== 开始录音 ==="); - -// if (isRecordingRef.current) { -// console.log("已在录音,忽略"); -// return; -// } - -// if (!isInitialized || !initializedRef.current) { -// Toast.show("应用正在初始化,请稍后重试"); -// return; -// } - -// if (!hasPermission) { -// const granted = await checkMicrophonePermission(true); -// if (!granted) { -// return; -// } -// } - -// try { -// // 震动反馈 - 开始录音时轻微震动 -// vibrate(50); - -// // 播放开始录音音效 -// playSound(startRecordSoundRef); - -// // 立即更新UI状态 -// setIsRecording(true); -// setRecordingDuration(0); -// isCancelledRef.current = false; -// recordingStartTimeRef.current = Date.now(); - -// stopAllAudio(); - -// // 使用react-audio-voice-recorder开始录音 -// recorderControls.startRecording(); - -// // 立即开始计时 -// recordingTimerRef.current = setInterval(() => { -// setRecordingDuration((prev) => prev + 1); -// }, 1000); - -// Toast.show("开始录音..."); -// } catch (error) { -// console.error("开始录音失败:", error); -// resetRecordingState(); - -// if ( -// error.name === "NotAllowedError" || -// error.name === "PermissionDeniedError" -// ) { -// setHasPermission(false); -// if (!dialogShowing) { -// showPermissionDialog(); -// } -// } else { -// Toast.show(`录音失败: ${error.message || "未知错误"}`); -// } -// } -// }, [ -// hasPermission, -// isInitialized, -// recorderControls, -// checkMicrophonePermission, -// dialogShowing, -// showPermissionDialog, -// ]); - -// const stopRecording = useCallback(() => { -// console.log("=== 停止录音并发送 ==="); - -// if (!isRecordingRef.current) { -// console.log("未在录音,忽略"); -// return; -// } - -// try { -// // 震动反馈 - 发送时的震动模式:短-停-短 -// vibrate([100, 50, 100]); - -// // 立即更新UI状态 -// setIsRecording(false); - -// if (recordingTimerRef.current) { -// clearInterval(recordingTimerRef.current); -// recordingTimerRef.current = undefined; -// } - -// // 停止录音并触发onRecordingComplete -// recorderControls.stopRecording(); -// } catch (error) { -// console.error("停止录音失败:", error); -// resetRecordingState(); -// Toast.show("停止录音失败"); -// } -// }, [recorderControls]); - -// // 在发送时检查录音时长 -// const onRecordingComplete = useCallback( -// (blob: Blob) => { -// console.log("=== 录音完成回调 ==="); -// console.log("录音完成状态:", { -// blobSize: blob.size, -// isCancelled: isCancelledRef.current, -// recordingDuration, -// }); - -// // 如果被取消,直接忽略,不做任何处理 -// if (isCancelledRef.current) { -// console.log("录音被取消,完全忽略结果"); -// return; -// } - -// // 计算实际录音时长 -// const actualDuration = Math.floor( -// (Date.now() - recordingStartTimeRef.current) / 1000 -// ); -// const finalDuration = Math.max(recordingDuration, actualDuration); - -// console.log("录音完成,准备发送:", { -// counterDuration: recordingDuration, -// actualDuration, -// finalDuration, -// }); - -// // 在这里检查录音时长 - 发送时拦截 -// if (finalDuration < 1) { -// console.log("录音时间太短,拒绝发送"); -// Toast.show("录音时间太短,请重新录音"); -// setRecordingDuration(0); -// return; -// } - -// // 检查blob有效性 -// if (!blob || blob.size === 0) { -// console.log("录音数据无效,拒绝发送"); -// Toast.show("录音数据无效,请重新录音"); -// setRecordingDuration(0); -// return; -// } - -// // 通过所有检查,发送消息 -// // 播放发送成功音效 -// playSound(sendSoundRef); - -// // 发送成功的震动反馈 - 长震动表示成功 -// vibrate(200); - -// const audioUrl = URL.createObjectURL(blob); -// const newMessage: Message = { -// id: Date.now().toString(), -// type: "voice", -// audioUrl, -// duration: finalDuration, -// timestamp: Date.now(), -// isTranslating: true, -// }; - -// console.log("发送语音消息:", newMessage); -// setMessages((prev) => [...prev, newMessage]); -// setRecordingDuration(0); - -// setTimeout(() => { -// translateAudio(newMessage.id, blob); -// }, 1000); - -// Toast.show("语音已发送"); -// }, -// [recordingDuration] -// ); - -// const cancelRecording = useCallback(() => { -// console.log("=== 取消录音 ==="); - -// // 取消时的震动反馈 - 三次短震动表示取消 -// vibrate([50, 50, 50, 50, 50]); - -// // 立即设置取消标志 - 这是最重要的 -// isCancelledRef.current = true; - -// // 立即显示取消提示 -// Toast.show("已取消录音"); - -// // 直接重置状态,不调用recorderControls.stopRecording() -// setIsRecording(false); -// setRecordingDuration(0); -// isRecordingRef.current = false; - -// if (recordingTimerRef.current) { -// clearInterval(recordingTimerRef.current); -// recordingTimerRef.current = undefined; -// } -// }, []); - -// const handleRecordButtonClick = useCallback( -// (e: React.MouseEvent) => { -// e.preventDefault(); -// e.stopPropagation(); - -// console.log("=== 录音按钮点击 ==="); - -// if (permissionChecking) { -// console.log("权限检查中,忽略点击"); -// return; -// } - -// if (isRecordingRef.current) { -// stopRecording(); -// } else { -// startRecording(); -// } -// }, -// [permissionChecking, startRecording, stopRecording] -// ); - -// const translateAudio = async (messageId: string, audioBlob: Blob) => { -// try { -// const translatedText = await mockTranslateAudio(audioBlob); - -// setMessages((prev) => -// prev.map((msg) => -// msg.id === messageId -// ? { ...msg, translatedText, isTranslating: false } -// : msg -// ) -// ); -// } catch (error) { -// console.error("翻译失败:", error); -// Toast.show("翻译失败,请重试"); - -// setMessages((prev) => -// prev.map((msg) => -// msg.id === messageId -// ? { -// ...msg, -// isTranslating: false, -// translatedText: "翻译失败,请重试", -// } -// : msg -// ) -// ); -// } -// }; - -// const mockTranslateAudio = async (audioBlob: Blob): Promise => { -// return new Promise((resolve) => { -// setTimeout(() => { -// const mockTranslations = [ -// "汪汪汪!我饿了,想吃东西!🍖", -// "喵喵~我想要抱抱!🤗", -// "我想出去玩耍!🎾", -// "我很开心!😊", -// "我有点害怕...😰", -// "我想睡觉了~😴", -// "主人,陪我玩一会儿吧!🎮", -// "我想喝水了💧", -// "外面有什么声音?👂", -// "我爱你,主人!❤️", -// ]; -// const randomTranslation = -// mockTranslations[Math.floor(Math.random() * mockTranslations.length)]; -// resolve(randomTranslation); -// }, 2000 + Math.random() * 2000); -// }); -// }; - -// const playAudio = (messageId: string, audioUrl: string) => { -// if (isRecording) { -// Toast.show("录音中,无法播放音频"); -// return; -// } - -// if (currentPlayingId === messageId) { -// if (audioRefs.current[messageId]) { -// audioRefs.current[messageId].pause(); -// audioRefs.current[messageId].currentTime = 0; -// } -// setCurrentPlayingId(null); -// setMessages((prev) => -// prev.map((msg) => -// msg.id === messageId ? { ...msg, isPlaying: false } : msg -// ) -// ); -// return; -// } - -// stopAllAudio(); - -// if (!audioRefs.current[messageId]) { -// audioRefs.current[messageId] = new Audio(audioUrl); -// } - -// const audio = audioRefs.current[messageId]; -// audio.currentTime = 0; - -// audio.onended = () => { -// setCurrentPlayingId(null); -// setMessages((prev) => -// prev.map((msg) => -// msg.id === messageId ? { ...msg, isPlaying: false } : msg -// ) -// ); -// }; - -// audio.onerror = (error) => { -// console.error("音频播放错误:", error); -// Toast.show("音频播放失败"); -// setCurrentPlayingId(null); -// setMessages((prev) => -// prev.map((msg) => -// msg.id === messageId ? { ...msg, isPlaying: false } : msg -// ) -// ); -// }; - -// audio -// .play() -// .then(() => { -// setCurrentPlayingId(messageId); -// setMessages((prev) => -// prev.map((msg) => -// msg.id === messageId -// ? { ...msg, isPlaying: true } -// : { ...msg, isPlaying: false } -// ) -// ); -// }) -// .catch((error) => { -// console.error("音频播放失败:", error); -// Toast.show("音频播放失败"); -// }); -// }; - -// const formatDuration = (seconds: number) => { -// const mins = Math.floor(seconds / 60); -// const secs = seconds % 60; -// return `${mins}:${secs.toString().padStart(2, "0")}`; -// }; - -// const formatTime = (timestamp: number) => { -// return new Date(timestamp).toLocaleTimeString("zh-CN", { -// hour: "2-digit", -// minute: "2-digit", -// }); -// }; - -// return ( -//
-// {/* 调试信息 */} -// {process.env.NODE_ENV === "development" && ( -//
-//
初始化中: {initializingRef.current ? "⏳" : "✅"}
-//
已初始化: {initializedRef.current ? "✅" : "❌"}
-//
状态初始化: {isInitialized ? "✅" : "❌"}
-//
-// 权限: {hasPermission === null ? "⏳" : hasPermission ? "✅" : "❌"} -//
-//
录音: {isRecording ? "🔴" : "⚪"}
-//
权限检查: {permissionChecking ? "⏳" : "✅"}
-//
弹窗: {dialogShowing ? "📱" : "❌"}
-//
已取消: {isCancelledRef.current ? "✅" : "❌"}
-//
录音时长: {recordingDuration}s
-//
震动支持: {"vibrate" in navigator ? "✅" : "❌"}
-//
-// )} - -// {/* 头部 */} -//
-//
🐾
-//
-//
我的宠物
-//
-// {initializingRef.current -// ? "初始化中..." -// : !isInitialized -// ? "等待初始化..." -// : permissionChecking -// ? "检查权限中..." -// : hasPermission === null -// ? "权限状态未知" -// : hasPermission -// ? "宠物语音翻译器" -// : "需要麦克风权限"} -//
-//
-//
- -// {/* 消息列表 */} -//
-// {messages.length === 0 ? ( -//
-//
🎤
-//
-// {initializingRef.current -// ? "正在初始化..." -// : !isInitialized -// ? "等待初始化..." -// : permissionChecking -// ? "正在检查权限..." -// : hasPermission === null -// ? "权限状态未知" -// : !hasPermission -// ? "请授权麦克风权限" -// : "点击下方按钮开始录音"} -//
-//
听听你的宠物想说什么~
-//
-// ) : ( -// messages.map((message) => ( -//
-//
-//
🐾
-//
-//
-//
playAudio(message.id, message.audioUrl)} -// > -//
-// {message.isPlaying ? ( -//
-// -// -// -//
-// ) : ( -// -// )} -//
-//
-// {formatDuration(message.duration)} -//
-//
-//
- -// {message.isTranslating ? ( -//
-//
-// 翻译中 -//
-// -// -// -//
-//
-//
-// ) : message.translatedText ? ( -//
-//
🐾 宠物说:
-//
-// {message.translatedText} -//
-//
-// ) : null} - -//
-// {formatTime(message.timestamp)} -//
-//
-//
-//
-// )) -// )} -//
-//
- -// {/* 录音控制区域 */} -//
-//
-// -//
- -//
-// {isRecording && ( -//
-//
-//
-// 录音中 {formatDuration(recordingDuration)} -//
-//
再次点击完成录音并发送
-//
-// )} - -//
-//
-// {isRecording && ( -// -// )} - -// -//
- -// {!isRecording && ( -//
-// {initializingRef.current -// ? "初始化中..." -// : !isInitialized -// ? "等待初始化..." -// : permissionChecking -// ? "检查权限中..." -// : !hasPermission -// ? "需要麦克风权限" -// : "点击录音"} -//
-// )} -//
-//
-//
-//
-// ); -// }; - -// export default PetVoiceTranslator; diff --git a/src/component/qr-scanner/index.tsx b/src/component/qr-scanner/index.tsx deleted file mode 100644 index 3aebcde..0000000 --- a/src/component/qr-scanner/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, {useRef, useEffect, useState} from 'react'; -import jsQR from 'jsqr'; - -export interface QRScannerProps { - width?: number; - height?: number; - onScan: (data: string) => void; -} - -/** - * 二维码扫描组件 - * @param width 画布宽度 - * @param height 画布高度 - * @param onScan 扫描成功的回调函数 - * @constructor QRScanner - */ -const QRScanner: React.FC = ({width = 300, height = 300, onScan}) => { - const videoRef = useRef(null); - const canvasRef = useRef(null); - const [isScanning, setIsScanning] = useState(true); - const [scanSuccess, setScanSuccess] = useState(false); // 新的状态变量 - let streamRef: MediaStream | null = null; // 存储摄像头流的引用 - - useEffect(() => { - navigator.mediaDevices.getUserMedia({video: {facingMode: "environment"}}) - .then(stream => { - streamRef = stream; // 存储对摄像头流的引用 - if (videoRef.current) { - videoRef.current.srcObject = stream; - videoRef.current.addEventListener('loadedmetadata', () => { - videoRef.current?.play().then(() => { - requestAnimationFrame(tick); - }).catch(err => console.error("Error playing video: ", err)); - }); - } - }).catch(err => { - console.error("Error accessing media devices: ", err); - setIsScanning(false); - }); - }, []); - const tick = () => { - // 如果已经成功扫描,就不再继续执行tick - if (!isScanning) return; - - if (videoRef.current && canvasRef.current) { - if (videoRef.current.readyState === videoRef.current.HAVE_ENOUGH_DATA) { - let video = videoRef.current; - let canvas = canvasRef.current; - let ctx = canvas.getContext('2d'); - - if (ctx) { - canvas.height = height; - canvas.width = width; - ctx.drawImage(video, 0, 0, canvas.width, canvas.height); - - let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - let code = jsQR(imageData.data, imageData.width, imageData.height); - - if (code) { - onScan(code.data); // 扫码成功 - setIsScanning(false); // 更新状态为不再扫描 - setScanSuccess(true); // 显示扫描成功的蒙层 - if (streamRef) { - let tracks = streamRef.getTracks(); - tracks.forEach(track => track.stop()); // 关闭摄像头 - } - return; // 直接返回,避免再次调用 requestAnimationFrame - } - } - } - requestAnimationFrame(tick); - } - }; - return ( -
- - - {scanSuccess &&
识别成功!
} {/* 显示识别成功的蒙层 */} -
- ); -} - -export default QRScanner; diff --git a/src/component/voiceIcon/index.less b/src/component/voiceIcon/index.less deleted file mode 100644 index 3e40484..0000000 --- a/src/component/voiceIcon/index.less +++ /dev/null @@ -1,69 +0,0 @@ -/* VoiceIcon.css */ -.voice-icon { - display: flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - cursor: pointer; - gap: 2px; -} -.audio-recorder { - display: none !important; -} - -.wave { - width: 1px; - height: 13px; - background-color: rgba(0, 0, 0, 0.6); - border-radius: 2px; - transition: all 0.3s ease; -} - -.wave1 { - height: 4px; -} - -.wave2 { - height: 13px; -} - -.wave3 { - height: 4px; -} - -.wave3 { - height: 8px; -} -.wave4 { - height: 4px; -} - -/* 播放动画 */ -.voice-icon.playing .wave { - animation: voice-wave 1.2s ease-in-out infinite; -} - -.voice-icon.playing .wave1 { - animation-delay: 0s; -} - -.voice-icon.playing .wave2 { - animation-delay: 0.2s; -} - -.voice-icon.playing .wave3 { - animation-delay: 0.4s; -} - -@keyframes voice-wave { - 0%, - 100% { - transform: scaleY(0.3); - opacity: 0.5; - } - 50% { - transform: scaleY(1); - opacity: 1; - } -} diff --git a/src/component/voiceIcon/index.tsx b/src/component/voiceIcon/index.tsx deleted file mode 100644 index 09fb7a4..0000000 --- a/src/component/voiceIcon/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { useCallback, useState } from "react"; -import "./index.less"; - -const VoiceIcon = (props: { isPlaying: boolean; onChange?: () => void }) => { - const { isPlaying = false } = props; - const onChange = useCallback(() => { - props.onChange?.(); - }, [isPlaying]); - return ( -
-
-
-
-
-
- ); -}; - -export default React.memo(VoiceIcon); diff --git a/src/component/xpopup/index.less b/src/component/xpopup/index.less deleted file mode 100644 index b5b92ec..0000000 --- a/src/component/xpopup/index.less +++ /dev/null @@ -1,11 +0,0 @@ -.xpopup { - .adm-popup-body { - background-color: rgba(245, 245, 245, 1); - padding: 16px; - .header { - font-size: 16px; - display: flex; - justify-content: space-between; - } - } -} diff --git a/src/component/xpopup/index.tsx b/src/component/xpopup/index.tsx deleted file mode 100644 index 723cc11..0000000 --- a/src/component/xpopup/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Divider, Popup } from "antd-mobile"; -import { CloseOutline } from "antd-mobile-icons"; -import "./index.less"; - -interface DefinedProps { - visible: boolean; - title: string; - children: React.ReactNode; - onClose: () => void; -} -function XPopup(props: DefinedProps) { - const { visible, title, children, onClose } = props; - - return ( - -
-

{title}

- - - -
- -
{children}
-
- ); -} - -export default XPopup; diff --git a/src/composables/authorization.ts b/src/composables/authorization.ts deleted file mode 100644 index b622afa..0000000 --- a/src/composables/authorization.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {useState, useEffect} from 'react'; - -export const STORAGE_AUTHORIZE_KEY = 'token'; - -export const useAuthorization = () => { - const [currentToken, setCurrentToken] = useState(localStorage.getItem(STORAGE_AUTHORIZE_KEY)); - - // 同步 localStorage 变更 - useEffect(() => { - const handleStorageChange = (event: StorageEvent) => { - if (event.key === STORAGE_AUTHORIZE_KEY) { - setCurrentToken(localStorage.getItem(STORAGE_AUTHORIZE_KEY)); - } - }; - - window.addEventListener('storage', handleStorageChange); - return () => { - window.removeEventListener('storage', handleStorageChange); - }; - }, []); - - const login = (token: string) => { - localStorage.setItem(STORAGE_AUTHORIZE_KEY, token); - setCurrentToken(token); - }; - - const logout = () => { - localStorage.removeItem(STORAGE_AUTHORIZE_KEY); - setCurrentToken(null); - }; - - const isLogin = () => { - return !!currentToken; - }; - - return { - token: currentToken, - login, - logout, - isLogin - }; -}; diff --git a/src/composables/language.ts b/src/composables/language.ts deleted file mode 100644 index 9c808aa..0000000 --- a/src/composables/language.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {setDefaultConfig} from 'antd-mobile'; -import enUS from 'antd-mobile/es/locales/en-US'; -import zhCN from 'antd-mobile/es/locales/zh-CN'; - -function changeLanguage(language: string) { - let locale; - switch (language) { - case 'zh_CN': - locale = enUS; - break; - case 'en_US': - locale = zhCN; - break; - default: - locale = enUS; // 或者是你的默认语言 - } - - setDefaultConfig({locale: locale}); -} - -export default changeLanguage; diff --git a/src/enum/http-enum.ts b/src/enum/http-enum.ts deleted file mode 100644 index 03938d1..0000000 --- a/src/enum/http-enum.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @description: request method - */ -export enum RequestEnum { - GET = 'GET', - POST = 'POST', - PUT = 'PUT', - DELETE = 'DELETE', -} - -/** - * @description: contentType - */ -export enum ContentTypeEnum { - // json - JSON = 'application/json;charset=UTF-8', - // form-data qs - FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', - // form-data upload - FORM_DATA = 'multipart/form-data;charset=UTF-8', -} diff --git a/src/hooks/i18n.ts b/src/hooks/i18n.ts deleted file mode 100644 index ac3cd8c..0000000 --- a/src/hooks/i18n.ts +++ /dev/null @@ -1,12 +0,0 @@ -import zhCN from '@/locales/zh_CN.json' -import enUS from '@/locales/en_US.json' -import {useI18nStore} from '@/store/i18n'; - -export default function useI18n() { - const {lang} = useI18nStore(); - const locales = lang === 'en_US' ? enUS : zhCN; - - return (name: string) => { - return locales[name as keyof typeof locales] || name; - } -} diff --git a/src/hooks/location.ts b/src/hooks/location.ts deleted file mode 100644 index f549fc2..0000000 --- a/src/hooks/location.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {useState, useEffect} from 'react'; -import useI18n from "@/hooks/i18n"; - -// 定义位置坐标的类型 -export interface Coordinates { - lat: number; - lng: number; -} - -// 定义返回的位置状态的类型 -export interface LocationState { - loaded: boolean; - coordinates: Coordinates | null; -} - -// 定义错误状态的类型 -export type ErrorState = string | null; - -export const useLocation = (): { location: LocationState; error: ErrorState } => { - const t = useI18n(); - - // 用于存储位置信息和加载状态的状态 - const [location, setLocation] = useState({ - loaded: false, - coordinates: null, - }); - - // 用于存储任何错误消息的状态 - const [error, setError] = useState(null); - - // 地理位置成功处理函数 - const onSuccess = (location: GeolocationPosition) => { - setLocation({ - loaded: true, - coordinates: { - lat: location.coords.latitude, - lng: location.coords.longitude, - }, - }); - }; - - // 地理位置错误处理函数 - const onError = (error: GeolocationPositionError) => { - setError(error.message); - }; - - // 使用 useEffect 在组件挂载时执行地理位置请求 - useEffect(() => { - // 检查浏览器是否支持地理位置 - if (!navigator.geolocation) { - setError(t('hooks.location.unsupported')); - return; - } - - // 请求用户的当前位置 - navigator.geolocation.getCurrentPosition(onSuccess, onError); - }, []); - - return {location, error}; -}; diff --git a/src/hooks/session.ts b/src/hooks/session.ts deleted file mode 100644 index 5595df5..0000000 --- a/src/hooks/session.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {useState, useEffect} from 'react'; -import isEqual from 'lodash.isequal'; - -function useSessionStorage(key: string, initialValue: T): [T, (value: T) => void] { - // 初始化状态 - const [storedValue, setStoredValue] = useState(() => { - const item = sessionStorage.getItem(key); - if (item !== null) { - // 如果 sessionStorage 中有数据,则使用现有数据 - return JSON.parse(item); - } else { - // 当 sessionStorage 中没有相应的键时,使用 initialValue 初始化,并写入 sessionStorage - sessionStorage.setItem(key, JSON.stringify(initialValue)); - return initialValue; - } - }); - - // 监听并保存变化到 sessionStorage - useEffect(() => { - if (!isEqual(JSON.parse(sessionStorage.getItem(key) || 'null'), storedValue)) { - sessionStorage.setItem(key, JSON.stringify(storedValue)); - } - }, [key, storedValue]); - - return [storedValue, setStoredValue]; -} - -export default useSessionStorage; diff --git a/src/hooks/useFileUpload.ts b/src/hooks/useFileUpload.ts deleted file mode 100644 index a2f61f8..0000000 --- a/src/hooks/useFileUpload.ts +++ /dev/null @@ -1,198 +0,0 @@ -// hooks/useFileUpload.ts (更新) -import { useState, useCallback } from "react"; -import { - UploadConfig, - UploadProgress, - UploadResponse, - VoiceUploadStatus, -} from "../types/upload"; - -interface UseFileUploadReturn { - uploadStatus: VoiceUploadStatus; - uploadFile: ( - file: Blob, - fileName: string, - config: UploadConfig - ) => Promise; - resetUpload: () => void; -} - -export const useFileUpload = (): UseFileUploadReturn => { - const [uploadStatus, setUploadStatus] = useState({ - status: "idle", - }); - - // 检测文件类型并转换文件名 - const getFileExtension = (mimeType: string): string => { - const mimeToExt: Record = { - "audio/webm": ".webm", - "audio/mp4": ".m4a", - "audio/aac": ".aac", - "audio/wav": ".wav", - "audio/ogg": ".ogg", - "audio/mpeg": ".mp3", - }; - - // 处理带codecs的MIME类型 - const baseMimeType = mimeType.split(";")[0]; - return mimeToExt[baseMimeType] || ".webm"; - }; - - const uploadFile = useCallback( - async ( - file: Blob, - fileName: string, - config: UploadConfig - ): Promise => { - // 检查文件大小 - if (config.maxFileSize && file.size > config.maxFileSize) { - const error = `文件大小超过限制 (${Math.round( - config.maxFileSize / 1024 / 1024 - )}MB)`; - setUploadStatus({ - status: "error", - error, - }); - throw new Error(error); - } - - // 更宽松的文件类型检查,支持iOS格式 - const allowedTypes = config.allowedTypes || [ - "audio/webm", - "audio/mp4", - "audio/aac", - "audio/wav", - "audio/ogg", - "audio/mpeg", - ]; - - const baseMimeType = file.type.split(";")[0]; - const isTypeAllowed = allowedTypes.some( - (type) => baseMimeType === type || baseMimeType === type.split(";")[0] - ); - - if (!isTypeAllowed) { - console.warn(`文件类型 ${file.type} 不在允许列表中,但继续上传`); - } - - // 根据实际MIME类型调整文件名 - const extension = getFileExtension(file.type); - const adjustedFileName = fileName.replace(/\.[^/.]+$/, "") + extension; - - setUploadStatus({ - status: "uploading", - progress: { loaded: 0, total: file.size, percentage: 0 }, - }); - - return new Promise((resolve, reject) => { - const formData = new FormData(); - formData.append(config.fieldName || "file", file, adjustedFileName); - - // 添加额外的元数据 - formData.append("fileName", adjustedFileName); - formData.append("originalFileName", fileName); - formData.append("fileSize", file.size.toString()); - formData.append("fileType", file.type); - formData.append("uploadTime", new Date().toISOString()); - formData.append("userAgent", navigator.userAgent); - - const xhr = new XMLHttpRequest(); - - // 上传进度监听 - xhr.upload.addEventListener("progress", (event) => { - if (event.lengthComputable) { - const progress: UploadProgress = { - loaded: event.loaded, - total: event.total, - percentage: Math.round((event.loaded / event.total) * 100), - }; - - setUploadStatus({ - status: "uploading", - progress, - }); - } - }); - - // 上传完成监听 - xhr.addEventListener("load", () => { - try { - const response: UploadResponse = JSON.parse(xhr.responseText); - - if (xhr.status >= 200 && xhr.status < 300 && response.success) { - setUploadStatus({ - status: "success", - response, - progress: { - loaded: file.size, - total: file.size, - percentage: 100, - }, - }); - resolve(response); - } else { - const error = response.error || `上传失败: ${xhr.status}`; - setUploadStatus({ - status: "error", - error, - }); - reject(new Error(error)); - } - } catch (parseError) { - const error = "服务器响应格式错误"; - setUploadStatus({ - status: "error", - error, - }); - reject(new Error(error)); - } - }); - - // 上传错误监听 - xhr.addEventListener("error", () => { - const error = "网络错误,上传失败"; - setUploadStatus({ - status: "error", - error, - }); - reject(new Error(error)); - }); - - // 上传中断监听 - xhr.addEventListener("abort", () => { - const error = "上传已取消"; - setUploadStatus({ - status: "error", - error, - }); - reject(new Error(error)); - }); - - // 设置请求头 - const headers = { - "X-Requested-With": "XMLHttpRequest", - ...config.headers, - }; - - Object.entries(headers).forEach(([key, value]) => { - xhr.setRequestHeader(key, value); - }); - - // 发送请求 - xhr.open(config.method || "POST", config.url); - xhr.send(formData); - }); - }, - [] - ); - - const resetUpload = useCallback(() => { - setUploadStatus({ status: "idle" }); - }, []); - - return { - uploadStatus, - uploadFile, - resetUpload, - }; -}; diff --git a/src/http/axios-instance.ts b/src/http/axios-instance.ts deleted file mode 100644 index 2bef525..0000000 --- a/src/http/axios-instance.ts +++ /dev/null @@ -1,49 +0,0 @@ -import Axios, { - AxiosError, - AxiosInstance as AxiosType, - AxiosResponse, - InternalAxiosRequestConfig -} from 'axios'; -import {STORAGE_AUTHORIZE_KEY} from "@/composables/authorization.ts"; - -export interface ResponseBody { - code: number; - data?: T; - msg: string; -} - -async function requestHandler(config: InternalAxiosRequestConfig): Promise { - const token = localStorage.getItem(STORAGE_AUTHORIZE_KEY); - if (token) { - config.headers[STORAGE_AUTHORIZE_KEY] = token; - } - return config; -} - -function responseHandler(response: AxiosResponse): AxiosResponse { - // 响应拦截器逻辑... - return response; -} - -function errorHandler(error: AxiosError): Promise> { - // 错误处理逻辑... - return Promise.reject(error); -} - -class AxiosInstance { - private readonly instance: AxiosType; - - constructor(baseURL: string) { - this.instance = Axios.create({baseURL}); - - this.instance.interceptors.request.use(requestHandler, errorHandler); - this.instance.interceptors.response.use(responseHandler, errorHandler); - } - - public getInstance(): AxiosType { - return this.instance; - } -} - -const baseURL = import.meta.env.VITE_BASE_URL || 'http://127.0.0.1:8080'; -export const axiosInstance = new AxiosInstance(baseURL).getInstance(); diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 06b9df1..0000000 --- a/src/index.css +++ /dev/null @@ -1,103 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: "Roboto", sans-serif; -} - -a, -button, -input, -textarea { - -webkit-tap-highlight-color: transparent; -} - -html { - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; - font-size: 100%; -} - -/* 小屏幕设备(如手机)*/ -@media (max-width: 600px) { - html { - font-size: 90%; /* 字体稍微小一点 */ - } -} - -/* 中等屏幕设备(如平板)*/ -@media (min-width: 601px) and (max-width: 1024px) { - html { - font-size: 100%; /* 标准大小 */ - } -} - -/* 大屏幕设备(如桌面)*/ -@media (min-width: 1025px) { - html { - font-size: 110%; /* 字体稍微大一点 */ - } -} - -* { - -webkit-backface-visibility: hidden; - -moz-backface-visibility: hidden; - -ms-backface-visibility: hidden; -} - -a { - text-decoration: none; - color: inherit; -} - -img { - max-width: 100%; - height: auto; -} - -.ec-navbar { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 100; - background-color: #fff; - border-bottom: 1px solid #e5e5e5; - -webkit-transition: all 0.3s; - transition: all 0.3s; -} - -:root { - --primary-color: #ffc300; -} - -:root:root { - /* --adm-color-primary: #FFC300; - --adm-color-success: #00b578; - --adm-color-warning: #ff8f1f; - --adm-color-danger: #ff3141; - - --adm-color-white: #ffffff; - --adm-color-text: #333333; - --adm-color-text-secondary: #666666; - --adm-color-weak: #999999; - --adm-color-light: #cccccc; - --adm-color-border: #eeeeee; - --adm-color-box: #f5f5f5; - --adm-color-background: #ffffff; - - --adm-font-size-main: var(--adm-font-size-5); */ - - --adm-font-family: -apple-system, blinkmacsystemfont, "Helvetica Neue", - helvetica, segoe ui, arial, roboto, "PingFang SC", "miui", - "Hiragino Sans GB", "Microsoft Yahei", sans-serif; -} -.i-icon { - height: 100%; -} -svg { - height: 100%; -} diff --git a/src/layout/main/index.less b/src/layout/main/index.less deleted file mode 100644 index fe18e79..0000000 --- a/src/layout/main/index.less +++ /dev/null @@ -1,20 +0,0 @@ -.main-layout { - height: 100%; - .layout-content { - height: 100%; - overflow-y: auto; - // padding-bottom: 150px; - padding-bottom: constant(safe-area-inset-bottom); /*兼容 IOS<11.2*/ - padding-bottom: env(safe-area-inset-bottom); /*兼容 IOS>11.2*/ - } - - .layout-tab { - flex: 0 0 auto; - position: fixed; - bottom: 0; - width: 100%; - z-index: 1000; - background-color: #fff; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); - } -} diff --git a/src/layout/main/mainLayout.tsx b/src/layout/main/mainLayout.tsx deleted file mode 100644 index 6529c38..0000000 --- a/src/layout/main/mainLayout.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React from "react"; -import { NavBar, SafeArea, TabBar, Toast } from "antd-mobile"; -import { useNavigate, useLocation } from "react-router-dom"; -import { - ShoppingBag, - TransactionOrder, - User, - CattleZodiac, -} from "@icon-park/react"; -import "./index.less"; - -interface MainLayoutProps { - children: React.ReactNode; - isShowNavBar?: boolean; - title?: string; - onLink?: () => void; -} - -const MainLayout: React.FC = ({ - isShowNavBar, - children, - onLink, - title, -}) => { - const navigate = useNavigate(); - const location = useLocation(); - const { pathname } = location; - const [activeKey, setActiveKey] = React.useState(pathname); - - const setRouteActive = (value: string) => { - if (value !== "/") { - Toast.show("待开发"); - } - }; - - const tabs = [ - { - key: "/", - title: "宠物翻译", - icon: , - }, - { - key: "/set", - title: "待办", - icon: , - }, - { - key: "/message", - title: "消息", - icon: , - }, - { - key: "/me", - title: "我的", - - icon: , - }, - ]; - - const goBack = () => { - if (onLink) { - onLink?.(); - } else { - navigate(-1); - } - }; - return ( -
- - {isShowNavBar ? {title} : ""} -
{children}
- -
- {/* setRouteActive(value)} - safeArea={true} - > - {tabs.map((item) => ( - - ))} - */} -
-
- ); -}; - -export default MainLayout; diff --git a/src/locales/en_US.json b/src/locales/en_US.json deleted file mode 100644 index 373a607..0000000 --- a/src/locales/en_US.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "index.title": "Hello World!" -} diff --git a/src/locales/zh_CN.json b/src/locales/zh_CN.json deleted file mode 100644 index fb48ba5..0000000 --- a/src/locales/zh_CN.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "index.title": "你好,世界!" -} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx deleted file mode 100644 index 638931a..0000000 --- a/src/main.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import { BrowserRouter } from "react-router-dom"; -import App from "@/view/app/App.tsx"; -import "./index.css"; - -ReactDOM.createRoot(document.getElementById("root")!).render( - // - - - - // -); diff --git a/src/route/auth.tsx b/src/route/auth.tsx deleted file mode 100644 index 1406e73..0000000 --- a/src/route/auth.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, {useEffect} from 'react'; -import {useNavigate} from 'react-router-dom'; - -interface AuthRouteProps { - children: React.ReactNode; - auth?: boolean; -} - -/** - * 认证路由 - * @param children 子组件 - * @param auth 是否需要认证 - * @constructor 认证路由组件 - */ -const AuthRoute: React.FC = ({children, auth}) => { - const navigate = useNavigate(); - const token = localStorage.getItem('token'); // 或者其他认证令牌的获取方式 - const isAuthenticated = Boolean(token); // 认证逻辑 - - useEffect(() => { - if (auth && !isAuthenticated) { - navigate('/login'); // 如果未认证且路由需要认证,则重定向到登录 - } - }, [auth, isAuthenticated, navigate]); - - return <>{children}; -}; - -export default AuthRoute; diff --git a/src/route/render-routes.tsx b/src/route/render-routes.tsx deleted file mode 100644 index 12d6537..0000000 --- a/src/route/render-routes.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {Route, Routes} from 'react-router-dom'; -import {routes, AppRoute} from './routes'; -import AuthRoute from './auth.tsx'; - -/** - * 渲染路由 - * @constructor RenderRoutes - */ -export const RenderRoutes = () => { - const renderRoutes = (routes: AppRoute[]) => { - return routes.map(route => ( - - {route.element} - - } - > - {route.children && renderRoutes(route.children)} - - )); - }; - - return {renderRoutes(routes)}; -}; diff --git a/src/route/routes.tsx b/src/route/routes.tsx deleted file mode 100644 index 44b59ee..0000000 --- a/src/route/routes.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from "react"; -import Home from "@/view/home"; -import TranslateDetail from "@/view/home/detail"; -import Setting from "@/view/setting"; -import Page404 from "@/view/error/page404"; -export interface AppRoute { - path: string; - element: React.ReactNode; - auth?: boolean; - children?: AppRoute[]; -} - -export const routes: AppRoute[] = [ - { path: "/", element: , auth: false }, - { path: "/set", element: , auth: false }, - { path: "/detail", element: , auth: false }, - { path: "/mood", element: , auth: false }, - { path: "*", element: , auth: false }, -]; diff --git a/src/store/i18n.ts b/src/store/i18n.ts deleted file mode 100644 index c7b7aee..0000000 --- a/src/store/i18n.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {create} from 'zustand' -import {persist, createJSONStorage} from 'zustand/middleware' - -export const LANG_KEY = 'lang' -export const useI18nStore = create()( - persist( - (set, get) => ({ - lang: 'en_US', - changeLanguage: (lang) => set({lang}), - toggleLanguage: () => set(() => { - return { - lang: get().lang.includes('zh') ? 'en_US' : 'zh_CN' - } - }) - }), - { - name: 'i18n-storage', - storage: createJSONStorage(() => localStorage) - }, - ), -) \ No newline at end of file diff --git a/src/store/user.ts b/src/store/user.ts deleted file mode 100644 index f87b10e..0000000 --- a/src/store/user.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {create} from 'zustand' -import {persist, createJSONStorage} from 'zustand/middleware' - -export const useUserStore = create()( - persist( - (set) => ({ - user: { - name: '', - avatar: '', - email: '' - }, - setUser: (user: User) => set({user}), - clearUser: () => set({ - user: null - }), - }), - { - name: 'user-storage', - storage: createJSONStorage(() => localStorage) - }, - ), -) \ No newline at end of file diff --git a/src/types/chat.ts b/src/types/chat.ts deleted file mode 100644 index 22ec78a..0000000 --- a/src/types/chat.ts +++ /dev/null @@ -1,41 +0,0 @@ -// types/chat.ts -// types/chat.ts (更新) -export interface VoiceMessage { - id: string; - type: "voice"; - content: { - duration: number; - url?: string; - localId?: string; - blob?: Blob; - waveform?: number[]; - // 新增上传相关字段 - fileId?: string; - fileName?: string; - serverUrl?: string; - uploadStatus?: "uploading" | "success" | "error"; - uploadProgress?: number; - }; - sender: "user" | "pet"; - timestamp: number; - isPlaying?: boolean; - translation?: string; - translating?: boolean; -} - -export interface TextMessage { - id: string; - type: "text"; - content: string; - sender: "user" | "pet"; - timestamp: number; -} - -export type ChatMessage = VoiceMessage | TextMessage; - -export interface PetProfile { - name: string; - avatar: string; - species: "dog" | "cat" | "bird" | "other"; - personality: string; -} diff --git a/src/types/http.d.ts b/src/types/http.d.ts deleted file mode 100644 index 019239c..0000000 --- a/src/types/http.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -interface Page { - total: number; - size: number; - current: number; - pages: number; - records: T[]; -} - -export interface Result { - success: boolean; - code: number; - message: string; - data: T; -} \ No newline at end of file diff --git a/src/types/store.d.ts b/src/types/store.d.ts deleted file mode 100644 index c238023..0000000 --- a/src/types/store.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -interface User { - name: string; - avatar: string; - email: string; -} - -interface UserState { - user: User | null; - setUser: (user: User) => void; - clearUser: () => void; -} - -type lang = 'zh_CN' | 'en_US' - -interface LangStore { - lang: lang; - changeLanguage: (lang: lang) => void - toggleLanguage: () => void -} \ No newline at end of file diff --git a/src/types/upload.ts b/src/types/upload.ts deleted file mode 100644 index 64e0400..0000000 --- a/src/types/upload.ts +++ /dev/null @@ -1,34 +0,0 @@ -// types/upload.ts -export interface UploadConfig { - url: string; - method?: "POST" | "PUT"; - headers?: Record; - fieldName?: string; - maxFileSize?: number; - allowedTypes?: string[]; -} - -export interface UploadProgress { - loaded: number; - total: number; - percentage: number; -} - -export interface UploadResponse { - success: boolean; - data?: { - fileId: string; - fileName: string; - fileUrl: string; - duration: number; - size: number; - }; - error?: string; -} - -export interface VoiceUploadStatus { - status: "idle" | "uploading" | "success" | "error"; - progress?: UploadProgress; - response?: UploadResponse; - error?: string; -} diff --git a/src/utils/amount.ts b/src/utils/amount.ts deleted file mode 100644 index 6db3cae..0000000 --- a/src/utils/amount.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function toYuan(num: number) { - return (num / 100).toFixed(2); -} \ No newline at end of file diff --git a/src/utils/audioManager.ts b/src/utils/audioManager.ts deleted file mode 100644 index 044ba5d..0000000 --- a/src/utils/audioManager.ts +++ /dev/null @@ -1,208 +0,0 @@ -// utils/audioManager.ts -import { UniversalAudioPlayer } from "./audioPlayer"; - -export interface AudioInstance { - id: string; - player: UniversalAudioPlayer; - onStop?: () => void; - onPlay?: () => void; - onPause?: () => void; -} - -class AudioManager { - private static instance: AudioManager; - private currentAudio: AudioInstance | null = null; - private audioInstances: Map = new Map(); - - private constructor() {} - - static getInstance(): AudioManager { - if (!AudioManager.instance) { - AudioManager.instance = new AudioManager(); - } - return AudioManager.instance; - } - - // 注册音频实例 - registerAudio( - id: string, - player: UniversalAudioPlayer, - callbacks?: { - onStop?: () => void; - onPlay?: () => void; - onPause?: () => void; - } - ): void { - const audioInstance: AudioInstance = { - id, - player, - onStop: callbacks?.onStop, - onPlay: callbacks?.onPlay, - onPause: callbacks?.onPause, - }; - - this.audioInstances.set(id, audioInstance); - } - - // 注销音频实例 - unregisterAudio(id: string): void { - const audioInstance = this.audioInstances.get(id); - if (audioInstance) { - // 如果是当前播放的音频,先停止 - if (this.currentAudio?.id === id) { - this.stopCurrent(); - } - - // 销毁播放器 - audioInstance.player.destroy(); - this.audioInstances.delete(id); - } - } - - // 播放指定音频(会自动停止其他音频) - async playAudio(id: string): Promise { - const audioInstance = this.audioInstances.get(id); - if (!audioInstance) { - throw new Error(`音频实例 ${id} 不存在`); - } - - // 如果当前有其他音频在播放,先停止 - if (this.currentAudio && this.currentAudio.id !== id) { - this.stopCurrent(); - } - - try { - await audioInstance.player.play(); - this.currentAudio = audioInstance; - - // 触发播放回调 - audioInstance.onPlay?.(); - - console.log(`开始播放音频: ${id}`); - } catch (error) { - console.error(`播放音频 ${id} 失败:`, error); - throw error; - } - } - - // 暂停指定音频 - pauseAudio(id: string): void { - const audioInstance = this.audioInstances.get(id); - if (!audioInstance) { - console.warn(`音频实例 ${id} 不存在`); - return; - } - - audioInstance.player.pause(); - - // 如果是当前播放的音频,清除当前音频引用 - if (this.currentAudio?.id === id) { - this.currentAudio = null; - } - - // 触发暂停回调 - audioInstance.onPause?.(); - - console.log(`暂停音频: ${id}`); - } - - // 停止指定音频 - stopAudio(id: string): void { - const audioInstance = this.audioInstances.get(id); - if (!audioInstance) { - console.warn(`音频实例 ${id} 不存在`); - return; - } - - audioInstance.player.stop(); - - // 如果是当前播放的音频,清除当前音频引用 - if (this.currentAudio?.id === id) { - this.currentAudio = null; - } - - // 触发停止回调 - audioInstance.onStop?.(); - - console.log(`停止音频: ${id}`); - } - - // 停止当前播放的音频 - stopCurrent(): void { - if (this.currentAudio) { - this.stopAudio(this.currentAudio.id); - } - } - - // 暂停当前播放的音频 - pauseCurrent(): void { - if (this.currentAudio) { - this.pauseAudio(this.currentAudio.id); - } - } - - // 获取当前播放的音频ID - getCurrentAudioId(): string | null { - return this.currentAudio?.id || null; - } - - // 检查指定音频是否正在播放 - isPlaying(id: string): boolean { - const audioInstance = this.audioInstances.get(id); - if (!audioInstance) { - return false; - } - return audioInstance.player.getIsPlaying(); - } - - // 获取指定音频的播放时长 - getDuration(id: string): number { - const audioInstance = this.audioInstances.get(id); - if (!audioInstance) { - return 0; - } - return audioInstance.player.getDuration(); - } - - // 获取指定音频的当前播放时间 - getCurrentTime(id: string): number { - const audioInstance = this.audioInstances.get(id); - if (!audioInstance) { - return 0; - } - return audioInstance.player.getCurrentTime(); - } - - // 清理所有音频实例 - cleanup(): void { - this.audioInstances.forEach((audioInstance) => { - audioInstance.player.destroy(); - }); - this.audioInstances.clear(); - this.currentAudio = null; - } - - // 获取所有音频实例的状态 - getAudioStates(): Record< - string, - { - isPlaying: boolean; - duration: number; - currentTime: number; - } - > { - const states: Record = {}; - - this.audioInstances.forEach((audioInstance, id) => { - states[id] = { - isPlaying: audioInstance.player.getIsPlaying(), - duration: audioInstance.player.getDuration(), - currentTime: audioInstance.player.getCurrentTime(), - }; - }); - - return states; - } -} - -export default AudioManager; diff --git a/src/utils/audioPlayer.ts b/src/utils/audioPlayer.ts deleted file mode 100644 index e6c3315..0000000 --- a/src/utils/audioPlayer.ts +++ /dev/null @@ -1,246 +0,0 @@ -// utils/audioPlayer.ts -export class UniversalAudioPlayer { - private audio: HTMLAudioElement | null = null; - private audioContext: AudioContext | null = null; - private source: AudioBufferSourceNode | null = null; - private audioBuffer: AudioBuffer | null = null; - private isPlaying: boolean = false; - private startTime: number = 0; - private pauseTime: number = 0; - - constructor() { - this.initAudioContext(); - } - - private async initAudioContext(): Promise { - try { - if (this.isAndroid()) { - this.audioContext = new (window.AudioContext || - (window as any).webkitAudioContext)(); - - // 安卓需要用户交互来启动AudioContext - if (this.audioContext.state === "suspended") { - // 等待用户交互后再启动 - document.addEventListener( - "touchstart", - this.resumeAudioContext.bind(this), - { once: true } - ); - document.addEventListener( - "click", - this.resumeAudioContext.bind(this), - { once: true } - ); - } - } - } catch (error) { - console.warn("AudioContext初始化失败:", error); - } - } - - private async resumeAudioContext(): Promise { - if (this.audioContext && this.audioContext.state === "suspended") { - try { - await this.audioContext.resume(); - console.log("AudioContext已启动"); - } catch (error) { - console.error("AudioContext启动失败:", error); - } - } - } - - private isAndroid(): boolean { - return /Android/.test(navigator.userAgent); - } - - private isIOS(): boolean { - return /iPad|iPhone|iPod/.test(navigator.userAgent); - } - - async loadAudio(audioBlob: Blob): Promise { - try { - const audioUrl = URL.createObjectURL(audioBlob); - - if (this.isAndroid() && this.audioContext) { - // 安卓使用AudioContext播放 - await this.loadWithAudioContext(audioBlob); - } else { - // iOS和桌面使用HTMLAudioElement - await this.loadWithAudioElement(audioUrl); - } - } catch (error) { - console.error("音频加载失败:", error); - throw error; - } - } - - private async loadWithAudioContext(audioBlob: Blob): Promise { - if (!this.audioContext) { - throw new Error("AudioContext未初始化"); - } - - try { - const arrayBuffer = await audioBlob.arrayBuffer(); - this.audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer); - console.log("AudioContext音频加载成功"); - } catch (error) { - console.error("AudioContext音频解码失败:", error); - // 降级到HTMLAudioElement - const audioUrl = URL.createObjectURL(audioBlob); - await this.loadWithAudioElement(audioUrl); - } - } - - private async loadWithAudioElement(audioUrl: string): Promise { - return new Promise((resolve, reject) => { - this.audio = new Audio(audioUrl); - - // 安卓需要设置这些属性 - this.audio.preload = "auto"; - this.audio.controls = false; - - this.audio.addEventListener( - "canplaythrough", - () => { - console.log("HTMLAudioElement音频加载成功"); - resolve(); - }, - { once: true } - ); - - this.audio.addEventListener( - "error", - (e) => { - console.error("HTMLAudioElement音频加载失败:", e); - reject(new Error("音频加载失败")); - }, - { once: true } - ); - - // 安卓需要手动触发加载 - this.audio.load(); - }); - } - - async play(): Promise { - try { - if (this.audioBuffer && this.audioContext) { - // 使用AudioContext播放 - await this.playWithAudioContext(); - } else if (this.audio) { - // 使用HTMLAudioElement播放 - await this.playWithAudioElement(); - } else { - throw new Error("音频未加载"); - } - this.isPlaying = true; - } catch (error) { - console.error("音频播放失败:", error); - throw error; - } - } - - private async playWithAudioContext(): Promise { - if (!this.audioContext || !this.audioBuffer) { - throw new Error("AudioContext或AudioBuffer未准备好"); - } - - // 确保AudioContext已启动 - if (this.audioContext.state === "suspended") { - await this.audioContext.resume(); - } - - this.source = this.audioContext.createBufferSource(); - this.source.buffer = this.audioBuffer; - this.source.connect(this.audioContext.destination); - - this.source.onended = () => { - this.isPlaying = false; - }; - - this.startTime = this.audioContext.currentTime; - this.source.start(0); - } - - private async playWithAudioElement(): Promise { - if (!this.audio) { - throw new Error("Audio元素未准备好"); - } - - // 安卓需要重置音频位置 - this.audio.currentTime = 0; - - try { - await this.audio.play(); - } catch (error) { - // 如果自动播放失败,可能需要用户交互 - console.warn("自动播放失败,需要用户交互:", error); - throw new Error("播放失败,请点击播放按钮"); - } - } - - pause(): void { - if (this.source) { - this.source.stop(); - this.source = null; - } - - if (this.audio) { - this.audio.pause(); - } - - this.isPlaying = false; - } - - stop(): void { - this.pause(); - - if (this.audio) { - this.audio.currentTime = 0; - } - } - - getIsPlaying(): boolean { - return this.isPlaying; - } - - getDuration(): number { - if (this.audioBuffer) { - return this.audioBuffer.duration; - } - - if (this.audio) { - return this.audio.duration || 0; - } - - return 0; - } - - getCurrentTime(): number { - if (this.audioContext && this.startTime > 0) { - return this.audioContext.currentTime - this.startTime; - } - - if (this.audio) { - return this.audio.currentTime; - } - - return 0; - } - - destroy(): void { - this.stop(); - - if (this.audio) { - this.audio.src = ""; - this.audio = null; - } - - if (this.audioContext && this.audioContext.state !== "closed") { - this.audioContext.close().catch(console.error); - this.audioContext = null; - } - - this.audioBuffer = null; - } -} diff --git a/src/utils/audioRecorder.ts b/src/utils/audioRecorder.ts deleted file mode 100644 index 25d2027..0000000 --- a/src/utils/audioRecorder.ts +++ /dev/null @@ -1,362 +0,0 @@ -// utils/audioRecorder.ts -export interface AudioRecorderConfig { - sampleRate?: number; - channels?: number; - bitDepth?: number; -} - -export class UniversalAudioRecorder { - private mediaRecorder: MediaRecorder | null = null; - private audioChunks: BlobPart[] = []; - private stream: MediaStream | null = null; - private startTime: number = 0; - private pausedDuration: number = 0; - private pauseStartTime: number = 0; - private audioContext: AudioContext | null = null; - - constructor(private config: AudioRecorderConfig = {}) { - this.config = { - sampleRate: 44100, - channels: 1, - bitDepth: 16, - ...config, - }; - } - - // 设备检测 - private isIOS(): boolean { - return /iPad|iPhone|iPod/.test(navigator.userAgent); - } - - private isAndroid(): boolean { - return /Android/.test(navigator.userAgent); - } - - private isSafari(): boolean { - return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - } - - private isChrome(): boolean { - return ( - /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) - ); - } - - // 获取设备支持的最佳MIME类型 - private getSupportedMimeType(): string { - const isIOSDevice = this.isIOS(); - const isAndroidDevice = this.isAndroid(); - const isSafariBrowser = this.isSafari(); - const isChromeBrowser = this.isChrome(); - - let mimeTypes: string[] = []; - - if (isAndroidDevice) { - // 安卓设备优先使用这些格式 - mimeTypes = [ - "audio/webm;codecs=opus", - "audio/webm", - "audio/mp4;codecs=aac", - "audio/mp4", - "audio/aac", - "audio/ogg;codecs=opus", - "audio/wav", - ]; - } else if (isIOSDevice || isSafariBrowser) { - // iOS和Safari优先使用这些格式 - mimeTypes = [ - "audio/mp4;codecs=aac", - "audio/mp4", - "audio/aac", - "audio/wav", - "audio/webm;codecs=opus", - "audio/webm", - ]; - } else { - // 其他浏览器(主要是桌面Chrome/Firefox) - mimeTypes = [ - "audio/webm;codecs=opus", - "audio/webm", - "audio/mp4;codecs=aac", - "audio/mp4", - "audio/ogg;codecs=opus", - "audio/wav", - ]; - } - - for (const mimeType of mimeTypes) { - if (MediaRecorder.isTypeSupported(mimeType)) { - console.log( - `选择的音频格式: ${mimeType} (设备: ${this.getDeviceInfo()})` - ); - return mimeType; - } - } - - console.warn("没有找到明确支持的音频格式,使用浏览器默认格式"); - return ""; - } - - private getDeviceInfo(): string { - if (this.isAndroid()) return "Android"; - if (this.isIOS()) return "iOS"; - return "Desktop"; - } - - // 初始化AudioContext(安卓设备需要) - private async initAudioContext(): Promise { - try { - // 安卓设备需要AudioContext来确保音频正常工作 - if (this.isAndroid() && !this.audioContext) { - this.audioContext = new (window.AudioContext || - (window as any).webkitAudioContext)(); - - // 安卓需要用户交互来启动AudioContext - if (this.audioContext.state === "suspended") { - await this.audioContext.resume(); - } - } - } catch (error) { - console.warn("AudioContext初始化失败:", error); - } - } - - async start(): Promise { - try { - // 初始化AudioContext - await this.initAudioContext(); - - // 获取音频约束 - const audioConstraints = this.getAudioConstraints(); - - this.stream = await navigator.mediaDevices.getUserMedia(audioConstraints); - - // 安卓设备需要特殊处理 - if (this.isAndroid() && this.audioContext && this.stream) { - // 创建音频源节点以确保音频流正常 - const source = this.audioContext.createMediaStreamSource(this.stream); - const analyser = this.audioContext.createAnalyser(); - source.connect(analyser); - } - - const selectedMimeType = this.getSupportedMimeType(); - - // 创建MediaRecorder - const mediaRecorderOptions: MediaRecorderOptions = {}; - if (selectedMimeType) { - mediaRecorderOptions.mimeType = selectedMimeType; - } - - // 安卓使用较大的时间片,iOS使用中等时间片 - const timeslice = this.isAndroid() ? 1000 : this.isIOS() ? 1000 : 100; - - this.mediaRecorder = new MediaRecorder(this.stream, mediaRecorderOptions); - - this.audioChunks = []; - this.startTime = Date.now(); - this.pausedDuration = 0; - - this.mediaRecorder.ondataavailable = (event) => { - if (event.data.size > 0) { - this.audioChunks.push(event.data); - console.log(`收到音频数据: ${event.data.size} bytes`); - } - }; - - this.mediaRecorder.onerror = (event) => { - console.error("MediaRecorder错误:", event); - }; - - this.mediaRecorder.onstart = () => { - console.log("录音开始"); - }; - - this.mediaRecorder.onstop = () => { - console.log("录音停止"); - }; - - this.mediaRecorder.start(timeslice); - console.log("录音器启动成功:", { - mimeType: this.mediaRecorder.mimeType, - state: this.mediaRecorder.state, - device: this.getDeviceInfo(), - }); - } catch (error) { - this.cleanup(); - console.error("录音启动失败:", error); - throw new Error(`录音启动失败: ${error}`); - } - } - - private getAudioConstraints(): MediaStreamConstraints { - const isAndroidDevice = this.isAndroid(); - const isIOSDevice = this.isIOS(); - - if (isAndroidDevice) { - // 安卓设备使用更宽松的约束 - return { - audio: { - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - // 安卓不强制指定采样率和声道数 - }, - }; - } else if (isIOSDevice) { - // iOS设备约束 - return { - audio: { - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - }; - } else { - // 桌面设备可以使用更详细的约束 - return { - audio: { - sampleRate: this.config.sampleRate, - channelCount: this.config.channels, - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - }; - } - } - - stop(): Promise { - return new Promise((resolve, reject) => { - if (!this.mediaRecorder) { - reject(new Error("录音器未初始化")); - return; - } - - const timeout = setTimeout(() => { - reject(new Error("录音停止超时")); - }, 10000); // 增加超时时间 - - this.mediaRecorder.onstop = () => { - clearTimeout(timeout); - try { - console.log(`收集到 ${this.audioChunks.length} 个音频片段`); - - if (this.audioChunks.length === 0) { - reject(new Error("没有录制到音频数据")); - return; - } - - const mimeType = this.mediaRecorder?.mimeType || "audio/webm"; - const audioBlob = new Blob(this.audioChunks, { type: mimeType }); - - console.log("录音完成:", { - size: audioBlob.size, - type: audioBlob.type, - duration: this.getDuration(), - chunks: this.audioChunks.length, - }); - - // 验证blob是否有效 - if (audioBlob.size === 0) { - reject(new Error("录制的音频文件为空")); - return; - } - - this.cleanup(); - resolve(audioBlob); - } catch (error) { - this.cleanup(); - reject(error); - } - }; - - this.mediaRecorder.onerror = (event) => { - clearTimeout(timeout); - this.cleanup(); - reject(new Error("录音停止时发生错误")); - }; - - try { - if ( - this.mediaRecorder.state === "recording" || - this.mediaRecorder.state === "paused" - ) { - this.mediaRecorder.stop(); - } else { - clearTimeout(timeout); - reject(new Error("录音器状态异常")); - } - } catch (error) { - clearTimeout(timeout); - this.cleanup(); - reject(error); - } - }); - } - - pause(): void { - if (this.mediaRecorder && this.mediaRecorder.state === "recording") { - this.pauseStartTime = Date.now(); - this.mediaRecorder.pause(); - } - } - - resume(): void { - if (this.mediaRecorder && this.mediaRecorder.state === "paused") { - this.pausedDuration += Date.now() - this.pauseStartTime; - this.mediaRecorder.resume(); - } - } - - cancel(): void { - try { - if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") { - this.mediaRecorder.stop(); - } - } catch (error) { - console.error("取消录音时出错:", error); - } - this.cleanup(); - } - - getDuration(): number { - const currentTime = Date.now(); - const totalTime = currentTime - this.startTime - this.pausedDuration; - return Math.max(0, Math.floor(totalTime / 1000)); - } - - getState(): string { - return this.mediaRecorder?.state || "inactive"; - } - - // 获取支持的音频格式信息 - static getSupportedFormats(): string[] { - const formats = [ - "audio/webm;codecs=opus", - "audio/webm", - "audio/mp4;codecs=aac", - "audio/mp4", - "audio/aac", - "audio/wav", - "audio/ogg;codecs=opus", - "audio/ogg", - ]; - - return formats.filter((format) => MediaRecorder.isTypeSupported(format)); - } - - private cleanup(): void { - if (this.stream) { - this.stream.getTracks().forEach((track) => track.stop()); - this.stream = null; - } - - if (this.audioContext && this.audioContext.state !== "closed") { - this.audioContext.close().catch(console.error); - this.audioContext = null; - } - - this.mediaRecorder = null; - this.audioChunks = []; - } -} diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index 5a21bf8..0000000 --- a/src/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './amount'; -export * from './location'; \ No newline at end of file diff --git a/src/utils/js-audio-recorder.d.ts b/src/utils/js-audio-recorder.d.ts deleted file mode 100644 index a8b6c3c..0000000 --- a/src/utils/js-audio-recorder.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -declare module "js-audio-recorder" { - export interface RecorderOptions { - sampleBits?: number; - sampleRate?: number; - numChannels?: number; - compiling?: boolean; - } - - export default class Recorder { - constructor(options?: RecorderOptions); - start(): Promise; - pause(): void; - resume(): void; - stop(): void; - getBlob(): Blob; - getWAVBlob(): Blob; - destroy(): void; - duration: number; - fileSize: number; - isrecording: boolean; - } -} diff --git a/src/utils/location.ts b/src/utils/location.ts deleted file mode 100644 index 5a997c7..0000000 --- a/src/utils/location.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function toKm(m: number): number { - return m / 1000; -} \ No newline at end of file diff --git a/src/utils/voice.ts b/src/utils/voice.ts deleted file mode 100644 index f67276e..0000000 --- a/src/utils/voice.ts +++ /dev/null @@ -1,186 +0,0 @@ -// export interface VoiceMessage { -// id: string; -// audioUrl: string; -// audioBlob: Blob; -// duration: number; -// isPlaying: boolean; -// timestamp: number; -// type: "sent" | "received"; -// } - -// export interface WeChatVoiceRecorderProps { -// onSendVoice?: (voiceData: VoiceMessage) => void; -// maxDuration?: number; -// className?: string; -// } - -// export interface RecordingState { -// isRecording: boolean; -// isPaused: boolean; -// duration: number; -// hasPermission: boolean; -// } -export const mockTranslateAudio = async (): Promise => { - return new Promise((resolve) => { - setTimeout(() => { - const mockTranslations = [ - "汪汪汪!我饿了,想吃东西!🍖", - "喵喵~我想要抱抱!🤗", - "我想出去玩耍!🎾", - "我很开心!😊", - "我有点害怕...😰", - "我想睡觉了~😴", - "主人,陪我玩一会儿吧!🎮", - "我想喝水了💧", - "外面有什么声音?👂", - "我爱你,主人!❤️", - ]; - const randomTranslation = - mockTranslations[Math.floor(Math.random() * mockTranslations.length)]; - resolve(randomTranslation); - }, 2000 + Math.random() * 2000); - }); -}; -// 创建发送音效 - 清脆的"叮"声 -export const createSendSound = () => { - try { - const audioContext = new (window.AudioContext || - (window as any).webkitAudioContext)(); - const oscillator = audioContext.createOscillator(); - const gainNode = audioContext.createGain(); - - oscillator.connect(gainNode); - gainNode.connect(audioContext.destination); - - // 设置音调 - 清脆的高音 - oscillator.frequency.setValueAtTime(800, audioContext.currentTime); - oscillator.frequency.exponentialRampToValueAtTime( - 1200, - audioContext.currentTime + 0.1 - ); - - // 设置音量包络 - gainNode.gain.setValueAtTime(0, audioContext.currentTime); - gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.01); - gainNode.gain.exponentialRampToValueAtTime( - 0.01, - audioContext.currentTime + 0.3 - ); - - oscillator.type = "sine"; - - // 创建音频元素 - const audio = new Audio(); - - // 重写play方法来播放合成音效 - audio.play = () => { - return new Promise((resolve) => { - try { - const newOscillator = audioContext.createOscillator(); - const newGainNode = audioContext.createGain(); - - newOscillator.connect(newGainNode); - newGainNode.connect(audioContext.destination); - - newOscillator.frequency.setValueAtTime(800, audioContext.currentTime); - newOscillator.frequency.exponentialRampToValueAtTime( - 1200, - audioContext.currentTime + 0.1 - ); - - newGainNode.gain.setValueAtTime(0, audioContext.currentTime); - newGainNode.gain.linearRampToValueAtTime( - 0.3, - audioContext.currentTime + 0.01 - ); - newGainNode.gain.exponentialRampToValueAtTime( - 0.01, - audioContext.currentTime + 0.3 - ); - - newOscillator.type = "sine"; - newOscillator.start(audioContext.currentTime); - newOscillator.stop(audioContext.currentTime + 0.3); - - setTimeout(() => resolve(undefined), 300); - } catch (error) { - console.error("播放发送音效失败:", error); - resolve(undefined); - } - }); - }; - - return audio; - } catch (error) { - console.error("创建发送音效失败:", error); - return new Audio(); // 返回空音频对象 - } -}; -export const createStartRecordSound = () => { - try { - const audio = new Audio(); - - audio.play = () => { - return new Promise((resolve) => { - try { - const audioContext = new (window.AudioContext || - (window as any).webkitAudioContext)(); - const oscillator = audioContext.createOscillator(); - const gainNode = audioContext.createGain(); - - oscillator.connect(gainNode); - gainNode.connect(audioContext.destination); - - // 设置音调 - 低沉的音 - oscillator.frequency.setValueAtTime(400, audioContext.currentTime); - - // 设置音量包络 - gainNode.gain.setValueAtTime(0, audioContext.currentTime); - gainNode.gain.linearRampToValueAtTime( - 0.2, - audioContext.currentTime + 0.05 - ); - gainNode.gain.linearRampToValueAtTime( - 0, - audioContext.currentTime + 0.2 - ); - - oscillator.type = "sine"; - oscillator.start(audioContext.currentTime); - oscillator.stop(audioContext.currentTime + 0.2); - - setTimeout(() => resolve(undefined), 200); - } catch (error) { - console.error("播放录音音效失败:", error); - resolve(undefined); - } - }); - }; - - return audio; - } catch (error) { - console.error("创建录音音效失败:", error); - return new Audio(); - } -}; - -export const getAudioDuration = async (audioBlob: Blob): Promise => { - return new Promise((resolve, reject) => { - const AudioContextClass = - window.AudioContext || (window as any).webkitAudioContext; - const audioContext = new AudioContextClass(); - - const fileReader = new FileReader(); - fileReader.onload = async (e) => { - try { - const arrayBuffer = e.target?.result as ArrayBuffer; - const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); - const duration = audioBuffer.duration; - resolve(duration); - } catch (error) { - reject(error); - } - }; - fileReader.readAsArrayBuffer(audioBlob); - }); -}; diff --git a/src/view/app/App.css b/src/view/app/App.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/view/app/App.tsx b/src/view/app/App.tsx deleted file mode 100644 index 58c3075..0000000 --- a/src/view/app/App.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import "./App.css"; -import { RenderRoutes } from "@/route/render-routes.tsx"; -import { axiosInstance } from "@/http/axios-instance.ts"; -import { configure } from "axios-hooks"; -import enUS from "antd-mobile/es/locales/en-US"; -import { ConfigProvider } from "antd-mobile"; -import { useI18nStore } from "@/store/i18n.ts"; -import zhCN from "antd-mobile/es/locales/zh-CN"; - -function App() { - configure({ - axios: axiosInstance, - }); - const i18nStore = useI18nStore(); - return ( - <> - - - - - ); -} - -export default App; diff --git a/src/view/archives/index.tsx b/src/view/archives/index.tsx deleted file mode 100644 index bbfd98e..0000000 --- a/src/view/archives/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -// 档案 -import MainLayout from "@/layout/main/mainLayout"; -import { Button, Tabs } from "antd-mobile"; -import "./index.less"; - -function Index() { - return ( - -
-
- ); -} - -export default Index; diff --git a/src/view/error/page404.tsx b/src/view/error/page404.tsx deleted file mode 100644 index c9032f8..0000000 --- a/src/view/error/page404.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import MainLayout from "@/layout/main/mainLayout"; -import { Button } from "antd-mobile"; -import { useNavigate } from "react-router-dom"; -function Index() { - const navigate = useNavigate(); - const goHome = () => { - navigate("/"); - }; - return ( - - - - ); -} - -export default Index; diff --git a/src/view/home/component/message/index.less b/src/view/home/component/message/index.less deleted file mode 100644 index 3d6e700..0000000 --- a/src/view/home/component/message/index.less +++ /dev/null @@ -1,46 +0,0 @@ -.message { - flex: 1 auto; - overflow-y: auto; - padding: 12px; - .item { - color: rgba(0, 0, 0, 0.45); - font-size: 12px; - display: flex; - gap: 12px; - margin-bottom: 20px; - } - .avatar { - width: 40px; - height: 40px; - border-radius: 40px; - background: #eee; - } - .voice-container { - display: flex; - align-items: center; - justify-content: space-between; - background: rgba(0, 0, 0, 0.02); - border: 1px solid rgba(0, 0, 0, 0.06); - border-radius: 8px; - margin-top: 8px; - padding-right: 12px; - .time { - color: rgba(0, 0, 0, 0.88); - } - .tips { - // align-self: stretch; - // display: grid; - // align-items: center; - color: rgba(0, 0, 0, 0.45); - } - } - .translate { - display: flex; - gap: 12px; - padding: 12px; - align-items: center; - background: rgba(0, 0, 0, 0.02); - border-radius: 8px; - margin-top: 12px; - } -} diff --git a/src/view/home/component/message/index.tsx b/src/view/home/component/message/index.tsx deleted file mode 100644 index aae218e..0000000 --- a/src/view/home/component/message/index.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { Divider, Image, SpinLoading, Toast } from "antd-mobile"; -import VoiceIcon from "@/component/voiceIcon"; -import { Message } from "../../types"; -import dogSvg from "@/assets/translate/dog.svg"; -import catSvg from "@/assets/translate/cat.svg"; -import pigSvg from "@/assets/translate/pig.svg"; -import "./index.less"; -import { useEffect, useRef, useState } from "react"; - -interface DefinedProps { - data: Message[]; - isRecording: boolean; -} - -function Index(props: DefinedProps) { - const { data, isRecording } = props; - const audioRefs = useRef<{ [key: string]: HTMLAudioElement }>({}); - const [isPlaying, setIsPlating] = useState(false); - const [currentPlayingId, setCurrentPlayingId] = useState(); - - useEffect(() => { - if (isRecording) { - stopAllAudio(); - } - }, [isRecording]); - const onVoiceChange = () => { - setIsPlating(!isPlaying); - }; - const playAudio = (messageId: number, audioUrl: string) => { - if (isRecording) { - Toast.show("录音中,无法播放音频"); - return; - } - - if (currentPlayingId === messageId) { - if (audioRefs.current[messageId]) { - audioRefs.current[messageId].pause(); - audioRefs.current[messageId].currentTime = 0; - } - setCurrentPlayingId(undefined); - setIsPlating(false); - return; - } - - stopAllAudio(); - if (!audioRefs.current[messageId]) { - audioRefs.current[messageId] = new Audio(audioUrl); - } - - const audio = audioRefs.current[messageId]; - audio.currentTime = 0; - - audio.onended = () => { - setCurrentPlayingId(undefined); - setIsPlating(false); - }; - - audio.onerror = (error) => { - console.error("音频播放错误:", error); - Toast.show("音频播放失败"); - setIsPlating(false); - }; - - audio - .play() - .then(() => { - setCurrentPlayingId(messageId); - setIsPlating(true); - }) - .catch((error) => { - console.error("音频播放失败:", error); - Toast.show("音频播放失败"); - }); - }; - const stopAllAudio = () => { - if (currentPlayingId && audioRefs.current[currentPlayingId]) { - audioRefs.current[currentPlayingId].pause(); - audioRefs.current[currentPlayingId].currentTime = 0; - setIsPlating(false); - setCurrentPlayingId(undefined); - } - - Object.values(audioRefs.current).forEach((audio) => { - if (!audio.paused) { - audio.pause(); - audio.currentTime = 0; - } - }); - }; - const renderAvatar = (type?: "pig" | "cat" | "dog") => { - if (type === "pig") { - ; - } - if (type === "cat") { - return ( - - ); - } - return ( - - ); - }; - - return ( -
- {data.map((item, index) => ( -
playAudio(item.id, item.audioUrl)} - > - {renderAvatar(item.type)} -
-
- {item.name} - - {item.timestamp} -
-
- -
{item.duration}''
-
- {item.isTranslating ? ( -
- - 翻译中... -
- ) : ( -
{item.translatedText}
- )} -
-
- ))} - -
-
-
-
- 生无可恋喵 - - 15:00 -
-
- -
- {isRecording ? "录制中..." : "轻点麦克风录制"} -
-
-
-
-
- ); -} - -export default Index; diff --git a/src/view/home/component/search/index.less b/src/view/home/component/search/index.less deleted file mode 100644 index 7289ace..0000000 --- a/src/view/home/component/search/index.less +++ /dev/null @@ -1,22 +0,0 @@ -.search { - display: flex; - gap: 12px; - width: 100%; - .adm-search-bar-input-box-icon { - display: flex; - align-items: center; - } - .adm-search-bar { - flex: 1 auto; - } - .all { - height: 34px !important; - background: var(--adm-color-fill-content); - border-radius: 6px; - padding: 0px 12px; - display: flex; - align-items: center; - color: rgba(0, 0, 0, 0.88); - gap: 6px; - } -} diff --git a/src/view/home/component/search/index.tsx b/src/view/home/component/search/index.tsx deleted file mode 100644 index d371724..0000000 --- a/src/view/home/component/search/index.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { DownOne } from "@icon-park/react"; -import { - ActionSheet, - Dropdown, - type DropdownRef, - Popup, - Radio, - SearchBar, - Space, -} from "antd-mobile"; -import { RadioValue } from "antd-mobile/es/components/radio"; -import { useRef, useState } from "react"; - -interface PropsConfig { - handleAllAni: () => void; -} -const allAni = ["全部宠物", "丑丑", "胖胖", "可可"]; -function Index(props: PropsConfig) { - const [aniName, setAniName] = useState("全部宠物"); - const animenuRef = useRef(null); - const handleAniSelect = (val: RadioValue) => { - setAniName(allAni[val as number]); - animenuRef.current?.close(); - }; - - return ( -
- - - -
- - - - 全部宠物 - - - 丑丑 - - - 胖胖 - - - 可可 - - - -
-
-
- {/*
- 全部宠物 - -
- setVisible(false)} - /> */} - {/* { - setVisible(false); - }} - > -
- 宠物 -
- 11111 -
*/} -
- ); -} - -export default Index; diff --git a/src/view/home/component/voice/index.less b/src/view/home/component/voice/index.less deleted file mode 100644 index 2cfcb68..0000000 --- a/src/view/home/component/voice/index.less +++ /dev/null @@ -1,54 +0,0 @@ -.voice-record { - position: relative; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - padding: 12px 0px; - box-shadow: 1px 2px 4px 3px #eee; - .adm-progress-circle-info { - height: 32px; - } - .isRecording { - .adm-progress-circle { - border-radius: 50%; - animation: recordingButtonPulse 1s infinite; - } - } - .tips { - color: #9f9f9f; - } - .circle { - display: inline-block; - width: 32px; - height: 32px; - border-radius: 8px; - background: rgba(22, 119, 255, 1); - } - - .cancleBtn { - position: absolute; - border: 2px solid rgba(230, 244, 255, 1); - width: 72px; - height: 72px; - right: 60px; - border-radius: 50%; - top: 50%; - transform: translateY(-50%); - } - - @keyframes recordingButtonPulse { - 0% { - box-shadow: 0 0 0 0 rgba(22, 119, 255, 0.5); - } - 30% { - box-shadow: 0 0 0 14px rgba(255, 77, 79, 0); - } - 70% { - box-shadow: 0 0 0 14px rgba(255, 77, 79, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(22, 119, 255, 0.5); - } - } -} diff --git a/src/view/home/component/voice/index.tsx b/src/view/home/component/voice/index.tsx deleted file mode 100644 index 196ec00..0000000 --- a/src/view/home/component/voice/index.tsx +++ /dev/null @@ -1,277 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { AudioRecorder, useAudioRecorder } from "react-audio-voice-recorder"; -import { Button, Dialog, Image, ProgressCircle, Toast } from "antd-mobile"; -import microphoneSvg from "@/assets/translate/microphone.svg"; -import microphoneDisabledSvg from "@/assets/translate/microphoneDisabledSvg.svg"; -import { createStartRecordSound, createSendSound } from "@/utils/voice"; -import "./index.less"; -import { Message } from "../../types"; -import { CloseCircleOutline } from "antd-mobile-icons"; - -interface DefinedProps { - onRecordingComplete: (url: string, finalDuration: number) => void; - isRecording: boolean; - onSetIsRecording: (flag: boolean) => void; -} -function Index(props: DefinedProps) { - const { isRecording } = props; - const [hasPermission, setHasPermission] = useState(false); //是否有权限 - const [isPermissioning, setIsPermissioning] = useState(true); //获取权限中 - const [recordingDuration, setRecordingDuration] = useState(0); //录音时长进度 - const [isModal, setIsModal] = useState(false); - const recordingTimerRef = useRef(); - const isCancelledRef = useRef(false); - const recordingStartTimeRef = useRef(0); //录音时长 - // 音效相关 - const sendSoundRef = useRef(null); - const startRecordSoundRef = useRef(null); - useEffect(() => { - initializeSounds(); - checkMicrophonePermission(); - }, [hasPermission]); - - useEffect(() => { - if (isRecording) { - recorderControls.startRecording(); - } else { - } - }, [isRecording]); - - //重置状态 - const onResetRecordingState = () => { - props.onSetIsRecording(false); - setRecordingDuration(0); - recordingStartTimeRef.current = 0; - if (recordingTimerRef.current) { - clearInterval(recordingTimerRef.current); - recordingTimerRef.current = undefined; - } - }; - - const initializeSounds = () => { - try { - // 发送音效 - 使用Web Audio API生成 - sendSoundRef.current = createSendSound(); - - // 开始录音音效 - startRecordSoundRef.current = createStartRecordSound(); - - console.log("音效初始化完成"); - } catch (error) { - console.error("音效初始化失败:", error); - } - }; - - const renderBtn = useCallback(() => { - if (!hasPermission) { - //没有权限 - return ( - <> - - {isPermissioning ? ( -
获取麦克风权限中...
- ) : ( -
麦克风没有开启权限
- )} - - ); - } - if (isRecording) { - //正在录音中 - return ( -
- -
- -
-
- -
- ); - } else { - //麦克风状态 - return ( - - ); - } - }, [hasPermission, isRecording, recordingDuration]); - const checkMicrophonePermission = useCallback(async () => { - try { - if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { - setHasPermission(false); - setIsPermissioning(false); - Toast.show("浏览器不支持录音功能"); - return false; - } - - if (navigator.permissions && navigator.permissions.query) { - try { - const permissionStatus = await navigator.permissions.query({ - name: "microphone" as PermissionName, - }); - - if (permissionStatus.state === "denied") { - setHasPermission(false); - setIsPermissioning(false); - setIsModal(true); - - return false; - } - if (permissionStatus.state === "granted") { - setHasPermission(true); - setIsModal(false); - return true; - } - } catch (permError) { - console.log("权限查询不支持,继续使用getUserMedia检查"); - } - } - const stream = await navigator.mediaDevices.getUserMedia({ - audio: { - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - }); - - stream.getTracks().forEach((track) => track.stop()); - setHasPermission(true); - return true; - } catch (error: any) { - if (error.message.includes("user denied permission")) { - setIsModal(true); - } - - setHasPermission(false); - return false; - } - }, []); - - useEffect(() => { - if (isModal) { - Dialog.confirm({ - content: "重新获取麦克风权限", - onConfirm: async () => { - setIsModal(true); - await navigator.mediaDevices.getUserMedia({ - audio: { - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - }); - setHasPermission(true); - }, - }); - } - }, [isModal]); - - const recorderControls = useAudioRecorder( - { - noiseSuppression: true, - echoCancellation: true, - autoGainControl: true, - }, - (err) => { - console.error("录音错误:", err); - Toast.show("录音失败,请重试"); - onResetRecordingState(); - } - ); - - // 播放音效 - const playSound = async (soundRef: React.RefObject) => { - try { - if (soundRef.current) { - await soundRef.current.play(); - } - } catch (error: any) { - Toast.show(`播放音效失败:${error.message}`); - } - }; - - //开始录音 - const onStartRecording = () => { - isCancelledRef.current = false; - if (recordingTimerRef.current) { - clearInterval(recordingTimerRef.current); - recordingTimerRef.current = undefined; - } - props.onSetIsRecording(true); - // recorderControls.startRecording(); - recordingStartTimeRef.current = Date.now(); - // 立即开始计时 - recordingTimerRef.current = setInterval(() => { - setRecordingDuration((prev) => prev + 1); - }, 1000); - }; - const onStopRecording = useCallback(() => { - recorderControls.stopRecording(); - onResetRecordingState(); - }, [recorderControls, recordingDuration]); - //录音完成 - // 在发送时检查录音时长 - const onRecordingComplete = useCallback( - (blob: Blob) => { - if (isCancelledRef.current) { - Toast.show("已取消"); - return; - } - // 检查blob有效性 - if (!blob || blob.size === 0) { - Toast.show("录音数据无效,请重新录音"); - - return; - } - const audioUrl = URL.createObjectURL(blob); - const audio = new Audio(); - audio.src = audioUrl; - // 计算实际录音时长 - - audio.addEventListener("loadedmetadata", () => { - if (audio.duration < 1) { - Toast.show("录音时间太短,请重新录音"); - return; - } - alert(audio.duration); - playSound(sendSoundRef); - props.onRecordingComplete?.(audioUrl, Math.floor(audio.duration)); - }); - }, - [isCancelledRef, isRecording, sendSoundRef] - ); - - const cancelRecording = useCallback(() => { - isCancelledRef.current = true; - recorderControls.stopRecording(); - onResetRecordingState(); - }, []); - - return ( - <> - -
{renderBtn()}
- - ); -} - -export default React.memo(Index); diff --git a/src/view/home/detail.tsx b/src/view/home/detail.tsx deleted file mode 100644 index 2717d85..0000000 --- a/src/view/home/detail.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function Index() { - return
; -} - -export default Index; diff --git a/src/view/home/index.less b/src/view/home/index.less deleted file mode 100644 index e32b8cc..0000000 --- a/src/view/home/index.less +++ /dev/null @@ -1,39 +0,0 @@ -.home { - height: 100%; - overflow: hidden; - .adm-tabs { - display: flex; - flex-direction: column; - height: 100%; - } - .adm-tabs-content { - flex: 1; - overflow: hidden; - padding: 0px; - } - .adm-tabs-header { - border: 0 none; - position: sticky; - top: 0px; - background: #fff; - z-index: 99; - } - - .adm-tabs-tab { - font-size: 20px; - color: rgba(0, 0, 0, 0.25); - font-weight: 600; - &.adm-tabs-tab-active { - color: #000; - } - } - .adm-tabs-tab-line { - height: 0px; - } - - .translate-container { - display: flex; - flex-direction: column; - height: 100%; - } -} diff --git a/src/view/home/index.tsx b/src/view/home/index.tsx deleted file mode 100644 index 41f03b8..0000000 --- a/src/view/home/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import MainLayout from "@/layout/main/mainLayout"; -import { Button, Tabs } from "antd-mobile"; -import Translate from "./translate"; -import "./index.less"; - -function Index() { - const handleRecordComplete = (audioData: AudioData): void => { - console.log("录音完成:", audioData); - }; - - const handleError = (error: Error): void => { - console.error("录音错误:", error); - }; - - return ( - -
- - - - - - 2 - - -
-
- ); -} - -export default Index; diff --git a/src/view/home/translate.tsx b/src/view/home/translate.tsx deleted file mode 100644 index 77a928e..0000000 --- a/src/view/home/translate.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import FloatingMenu, { FloatMenuItemConfig } from "@/component/floatingMenu"; -import { Image, Toast } from "antd-mobile"; -import MessageCom from "./component/message"; -import VoiceRecord from "./component/voice"; -import XPopup from "@/component/xpopup"; -import type { Message } from "./types"; - -import { mockTranslateAudio } from "@/utils/voice"; -import dogSvg from "@/assets/translate/dog.svg"; -import catSvg from "@/assets/translate/cat.svg"; -import pigSvg from "@/assets/translate/pig.svg"; -import { MoreTwo } from "@icon-park/react"; -interface DefinedProps {} -const menuItems: FloatMenuItemConfig[] = [ - { icon: , type: "dog" }, - { icon: , type: "cat" }, - { icon: , type: "pig" }, - { - icon: ( - - ), - type: "add", - }, -]; -function Index(props: DefinedProps) { - // const data: Message[] = [ - // { - // id: 1, - // audioUrl: "", - // duration: 0, - // translatedText: "", - // isTranslating: true, - // isPlaying: false, - // timestamp: 0, - // }, - // ]; - const [currentPlayingId, setCurrentPlayingId] = useState(null); //当前播放id - const [messages, setMessages] = useState([]); - const [isRecording, setIsRecording] = useState(false); //是否录音中 - const [currentLanguage, setCurrentLanguage] = useState(); - const [visible, setVisible] = useState(false); - - useEffect(() => { - setCurrentLanguage(menuItems[0]); - }, []); - - //完成录音 - const onRecordingComplete = useCallback( - (audioUrl: string, actualDuration: number) => { - const newMessage: Message = { - id: Date.now(), - type: "dog", - audioUrl, - name: "生无可恋喵", - duration: actualDuration, - timestamp: Date.now(), - isTranslating: true, - }; - - setMessages((prev) => [...prev, newMessage]); - setTimeout(() => { - onTranslateAudio(newMessage.id); - }, 1000); - - Toast.show("语音已发送"); - }, - [messages] - ); - - //翻译 - const onTranslateAudio = useCallback( - async (messageId: number) => { - try { - const translatedText = await mockTranslateAudio(); - - setMessages((prev) => - prev.map((msg) => - msg.id === messageId - ? { ...msg, translatedText, isTranslating: false } - : msg - ) - ); - } catch (error) { - console.error("翻译失败:", error); - Toast.show("翻译失败,请重试"); - - setMessages((prev) => - prev.map((msg) => - msg.id === messageId - ? { - ...msg, - isTranslating: false, - translatedText: "翻译失败,请重试", - } - : msg - ) - ); - } - }, - [messages] - ); - - const onSetIsRecording = (flag: boolean) => { - setIsRecording(flag); - }; - - const onLanguage = (item: FloatMenuItemConfig) => { - if (item.type === "add") { - setVisible(true); - } else { - setCurrentLanguage(item); - } - }; - - return ( -
- - - - { - setVisible(false); - }} - > -
- 快捷选项 -
-
-
- 宠物语种 -
-
-
- ); -} - -export default Index; diff --git a/src/view/home/types.ts b/src/view/home/types.ts deleted file mode 100644 index 9e0d223..0000000 --- a/src/view/home/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface Message { - id: number; - type?: "dog" | "cat" | "pig"; - audioUrl: string; - name: string; //名字 - duration: number; //时长 - timestamp: number; //时间 - translatedText?: string; - isTranslating?: boolean; - avatar?: string; - isPlaying?: boolean; -} diff --git a/src/view/setting/index.tsx b/src/view/setting/index.tsx deleted file mode 100644 index b7cbbd5..0000000 --- a/src/view/setting/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import useI18n from "@/hooks/i18n.ts"; -import MainLayout from "@/layout/main/mainLayout"; -import { Button } from "antd-mobile"; -import { useI18nStore } from "@/store/i18n.ts"; - -function Index() { - const t = useI18n(); - const i18nStore = useI18nStore(); - - return qq; -} - -export default Index; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts deleted file mode 100644 index 62e4f5d..0000000 --- a/src/vite-env.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// - -interface ImportMetaEnv { - readonly VITE_BASE_URL: string; -} - -interface ImportMeta { - readonly env: ImportMetaEnv; -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index b3b7819..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", - "paths": { - "@/*": ["src/*"], - "@workspace/shared": ["packages/shared/src/index.ts"], - "@workspace/shared/*": ["packages/shared/src/*"], - "@workspace/utils": ["packages/utils/src/index.ts"], - "@workspace/utils/*": ["packages/utils/src/*"] - } - } -} diff --git a/tsconfig.node.json b/tsconfig.node.json deleted file mode 100644 index 4f625df..0000000 --- a/tsconfig.node.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"], - "exclude": ["dist", "node_modules"] -} diff --git a/types/global.d.ts b/types/global.d.ts new file mode 100644 index 0000000..0aaab19 --- /dev/null +++ b/types/global.d.ts @@ -0,0 +1,19 @@ +// src/types/global.d.ts +declare module "*.module.less" { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module "*.module.css" { + const classes: { readonly [key: string]: string }; + export default classes; +} + +// 其他资源文件 +declare module "*.png"; +declare module "*.jpg"; +declare module "*.jpeg"; +declare module "*.gif"; +declare module "*.svg"; +declare module "*.ico"; +declare module "*.webp"; diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index b2e4138..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react-swc"; -import path from "path"; -import basicSsl from "@vitejs/plugin-basic-ssl"; -export default defineConfig({ - // plugins: [react()], - plugins: [react(), basicSsl()], - server: { - https: {}, - port: 3000, - host: "0.0.0.0", - open: true, - }, - - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - "~": "/src", - }, - }, -});