From 85244a451eaa1e8a5c3fb7079d24c6624c4b5329 Mon Sep 17 00:00:00 2001
From: wuxichen <17301714657@163.com>
Date: Fri, 5 Sep 2025 15:18:10 +0800
Subject: [PATCH] feat: init
---
.editorconfig | 16 +
.env | 1 -
.eslintrc.js | 42 -
README.md | 23 +-
eslint.config.js | 111 +
index.html | 39 -
package.json | 32 +-
packages/hooks/index.ts | 3 +
packages/hooks/package.json | 16 +
packages/hooks/src/i18n.ts | 12 -
packages/hooks/src/location.ts | 60 -
packages/hooks/src/useDocumentTitle.ts | 16 +
packages/shared/package.json | 7 -
.../src/error-page/errorBoundary}/index.less | 0
.../src/error-page/errorBoundary/index.tsx | 49 +
.../shared/src/error-page/index.module.less | 20 +
.../shared/src/error-page/notFound/index.tsx | 25 +
.../src/error-page/serverError/index.tsx | 29 +
packages/shared/src/xpopup/index.tsx | 2 +-
packages/shared/tsconfig.json | 26 +-
packages/shared/types/global.d.ts | 19 +
.../component => packages/styles}/index.less | 0
.../null.svg => packages/styles/mixins.less | 0
packages/styles/variables.less | 43 +
pnpm-lock.yaml | 2736 ++++++++++++++---
postcss.config.js | 13 -
projects/translate-h5/package.json | 40 -
projects/translate-h5/postcss.config.js | 14 +
projects/translate-h5/src/hooks/i18n.ts | 12 +
projects/translate-h5/src/hooks/location.ts | 63 +
.../translate-h5/src/hooks}/session.ts | 0
.../translate-h5/src/hooks}/useFileUpload.ts | 0
.../src/layout/main/mainLayout.tsx | 7 +-
projects/translate-h5/src/route/auth.tsx | 41 +-
.../translate-h5/src/route/render-routes.tsx | 59 +-
projects/translate-h5/src/route/routes.tsx | 36 +-
projects/translate-h5/src/types/global.d.ts | 19 +
.../src/view/home/component/search/index.tsx | 4 +-
.../translate-h5/src/view/home/index.less | 58 +-
projects/translate-h5/src/view/home/index.tsx | 8 +-
.../translate-h5/src/view/home/translate.tsx | 31 +-
.../translate-h5/src/view/setting/index.tsx | 13 -
projects/translate-h5/tsconfig.json | 1 -
projects/translate-h5/vite.config.ts | 38 +-
src/api/mock.ts | 37 -
src/assets/translate/cat.svg | 64 -
src/assets/translate/dog.svg | 42 -
src/assets/translate/microphone.svg | 6 -
.../translate/microphoneDisabledSvg.svg | 4 -
src/assets/translate/pig.svg | 49 -
src/assets/translate/playing.svg | 24 -
src/component/audioRecorder/archives.tsx | 5 -
.../audioRecorder/deviceCompatibility.tsx | 139 -
src/component/audioRecorder/index copy.tsx | 278 --
src/component/audioRecorder/index.less | 774 -----
src/component/audioRecorder/index.tsx | 275 --
.../audioRecorder/recordingStatusBar.tsx | 54 -
src/component/audioRecorder/translateItem.tsx | 11 -
src/component/audioRecorder/voiceMessage.tsx | 322 --
.../audioRecorder/voiceRecordButton.less | 350 ---
.../audioRecorder/voiceRecordButton.tsx | 248 --
src/component/carousel/index.less | 7 -
src/component/carousel/index.tsx | 49 -
src/component/floatingMenu/index.less | 42 -
src/component/floatingMenu/index.tsx | 194 --
.../petVoiceTranslator copy/index.less | 662 ----
.../petVoiceTranslator copy/index.tsx | 916 ------
src/component/petVoiceTranslator/index.less | 660 ----
src/component/petVoiceTranslator/index.tsx | 1104 -------
src/component/qr-scanner/index.tsx | 88 -
src/component/voiceIcon/index.less | 69 -
src/component/voiceIcon/index.tsx | 22 -
src/component/xpopup/index.less | 11 -
src/component/xpopup/index.tsx | 28 -
src/composables/authorization.ts | 42 -
src/composables/language.ts | 21 -
src/enum/http-enum.ts | 21 -
src/hooks/i18n.ts | 12 -
src/hooks/location.ts | 60 -
src/hooks/session.ts | 28 -
src/hooks/useFileUpload.ts | 198 --
src/http/axios-instance.ts | 49 -
src/index.css | 103 -
src/layout/main/index.less | 20 -
src/layout/main/mainLayout.tsx | 88 -
src/locales/en_US.json | 3 -
src/locales/zh_CN.json | 3 -
src/main.tsx | 13 -
src/route/auth.tsx | 29 -
src/route/render-routes.tsx | 27 -
src/route/routes.tsx | 19 -
src/store/i18n.ts | 21 -
src/store/user.ts | 22 -
src/types/chat.ts | 41 -
src/types/http.d.ts | 14 -
src/types/store.d.ts | 19 -
src/types/upload.ts | 34 -
src/utils/amount.ts | 3 -
src/utils/audioManager.ts | 208 --
src/utils/audioPlayer.ts | 246 --
src/utils/audioRecorder.ts | 362 ---
src/utils/index.ts | 2 -
src/utils/js-audio-recorder.d.ts | 22 -
src/utils/location.ts | 3 -
src/utils/voice.ts | 186 --
src/view/app/App.css | 0
src/view/app/App.tsx | 24 -
src/view/archives/index.tsx | 14 -
src/view/error/page404.tsx | 18 -
src/view/home/component/message/index.less | 46 -
src/view/home/component/message/index.tsx | 176 --
src/view/home/component/search/index.less | 22 -
src/view/home/component/search/index.tsx | 87 -
src/view/home/component/voice/index.less | 54 -
src/view/home/component/voice/index.tsx | 277 --
src/view/home/detail.tsx | 5 -
src/view/home/index.less | 39 -
src/view/home/index.tsx | 31 -
src/view/home/translate.tsx | 154 -
src/view/home/types.ts | 12 -
src/view/setting/index.tsx | 13 -
src/vite-env.d.ts | 9 -
tsconfig.json | 29 -
tsconfig.node.json | 11 -
types/global.d.ts | 19 +
vite.config.ts | 21 -
126 files changed, 3020 insertions(+), 10278 deletions(-)
create mode 100644 .editorconfig
delete mode 100644 .env
delete mode 100644 .eslintrc.js
create mode 100644 eslint.config.js
delete mode 100644 index.html
create mode 100644 packages/hooks/package.json
delete mode 100644 packages/hooks/src/i18n.ts
delete mode 100644 packages/hooks/src/location.ts
create mode 100644 packages/hooks/src/useDocumentTitle.ts
rename {src/view/archives => packages/shared/src/error-page/errorBoundary}/index.less (100%)
create mode 100644 packages/shared/src/error-page/errorBoundary/index.tsx
create mode 100644 packages/shared/src/error-page/index.module.less
create mode 100644 packages/shared/src/error-page/notFound/index.tsx
create mode 100644 packages/shared/src/error-page/serverError/index.tsx
create mode 100644 packages/shared/types/global.d.ts
rename {src/view/home/component => packages/styles}/index.less (100%)
rename public/null.svg => packages/styles/mixins.less (100%)
create mode 100644 packages/styles/variables.less
delete mode 100644 postcss.config.js
create mode 100644 projects/translate-h5/postcss.config.js
create mode 100644 projects/translate-h5/src/hooks/i18n.ts
create mode 100644 projects/translate-h5/src/hooks/location.ts
rename {packages/hooks/src => projects/translate-h5/src/hooks}/session.ts (100%)
rename {packages/hooks/src => projects/translate-h5/src/hooks}/useFileUpload.ts (100%)
create mode 100644 projects/translate-h5/src/types/global.d.ts
delete mode 100644 projects/translate-h5/src/view/setting/index.tsx
delete mode 100644 src/api/mock.ts
delete mode 100644 src/assets/translate/cat.svg
delete mode 100644 src/assets/translate/dog.svg
delete mode 100644 src/assets/translate/microphone.svg
delete mode 100644 src/assets/translate/microphoneDisabledSvg.svg
delete mode 100644 src/assets/translate/pig.svg
delete mode 100644 src/assets/translate/playing.svg
delete mode 100644 src/component/audioRecorder/archives.tsx
delete mode 100644 src/component/audioRecorder/deviceCompatibility.tsx
delete mode 100644 src/component/audioRecorder/index copy.tsx
delete mode 100644 src/component/audioRecorder/index.less
delete mode 100644 src/component/audioRecorder/index.tsx
delete mode 100644 src/component/audioRecorder/recordingStatusBar.tsx
delete mode 100644 src/component/audioRecorder/translateItem.tsx
delete mode 100644 src/component/audioRecorder/voiceMessage.tsx
delete mode 100644 src/component/audioRecorder/voiceRecordButton.less
delete mode 100644 src/component/audioRecorder/voiceRecordButton.tsx
delete mode 100644 src/component/carousel/index.less
delete mode 100644 src/component/carousel/index.tsx
delete mode 100644 src/component/floatingMenu/index.less
delete mode 100644 src/component/floatingMenu/index.tsx
delete mode 100644 src/component/petVoiceTranslator copy/index.less
delete mode 100644 src/component/petVoiceTranslator copy/index.tsx
delete mode 100644 src/component/petVoiceTranslator/index.less
delete mode 100644 src/component/petVoiceTranslator/index.tsx
delete mode 100644 src/component/qr-scanner/index.tsx
delete mode 100644 src/component/voiceIcon/index.less
delete mode 100644 src/component/voiceIcon/index.tsx
delete mode 100644 src/component/xpopup/index.less
delete mode 100644 src/component/xpopup/index.tsx
delete mode 100644 src/composables/authorization.ts
delete mode 100644 src/composables/language.ts
delete mode 100644 src/enum/http-enum.ts
delete mode 100644 src/hooks/i18n.ts
delete mode 100644 src/hooks/location.ts
delete mode 100644 src/hooks/session.ts
delete mode 100644 src/hooks/useFileUpload.ts
delete mode 100644 src/http/axios-instance.ts
delete mode 100644 src/index.css
delete mode 100644 src/layout/main/index.less
delete mode 100644 src/layout/main/mainLayout.tsx
delete mode 100644 src/locales/en_US.json
delete mode 100644 src/locales/zh_CN.json
delete mode 100644 src/main.tsx
delete mode 100644 src/route/auth.tsx
delete mode 100644 src/route/render-routes.tsx
delete mode 100644 src/route/routes.tsx
delete mode 100644 src/store/i18n.ts
delete mode 100644 src/store/user.ts
delete mode 100644 src/types/chat.ts
delete mode 100644 src/types/http.d.ts
delete mode 100644 src/types/store.d.ts
delete mode 100644 src/types/upload.ts
delete mode 100644 src/utils/amount.ts
delete mode 100644 src/utils/audioManager.ts
delete mode 100644 src/utils/audioPlayer.ts
delete mode 100644 src/utils/audioRecorder.ts
delete mode 100644 src/utils/index.ts
delete mode 100644 src/utils/js-audio-recorder.d.ts
delete mode 100644 src/utils/location.ts
delete mode 100644 src/utils/voice.ts
delete mode 100644 src/view/app/App.css
delete mode 100644 src/view/app/App.tsx
delete mode 100644 src/view/archives/index.tsx
delete mode 100644 src/view/error/page404.tsx
delete mode 100644 src/view/home/component/message/index.less
delete mode 100644 src/view/home/component/message/index.tsx
delete mode 100644 src/view/home/component/search/index.less
delete mode 100644 src/view/home/component/search/index.tsx
delete mode 100644 src/view/home/component/voice/index.less
delete mode 100644 src/view/home/component/voice/index.tsx
delete mode 100644 src/view/home/detail.tsx
delete mode 100644 src/view/home/index.less
delete mode 100644 src/view/home/index.tsx
delete mode 100644 src/view/home/translate.tsx
delete mode 100644 src/view/home/types.ts
delete mode 100644 src/view/setting/index.tsx
delete mode 100644 src/vite-env.d.ts
delete mode 100644 tsconfig.json
delete mode 100644 tsconfig.node.json
create mode 100644 types/global.d.ts
delete mode 100644 vite.config.ts
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 (
);
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) => (
-
-

-
- ))}
-
- );
-};
-
-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.isTranslating ? (
-
-
- 翻译中...
-
- ) : (
-
{item.translatedText}
- )}
-
-
- ))}
-
-
-
-
-
-
-
-
- {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",
- },
- },
-});