diff --git a/.editorconfig b/.editorconfig index e73bbcf5..c90a6350 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,7 +12,7 @@ insert_final_newline = true # Set default charset charset = utf-8 -# 2 space indentation +# 4 space indentation [*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}] indent_style = space indent_size = 4 @@ -21,4 +21,4 @@ indent_size = 4 charset = latin1 # Unfortunately, EditorConfig doesn't support space configuration inside import braces directly. -# You'll need to rely on your linter/formatter like ESLint or Prettier for that. +# You'll need to rely on your linter/formatter like ESLint or Prettier for that. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 522e2388..9063cd48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Develop node_modules/ package-lock.json +pnpm-lock.yaml out/ dist/ /src/core.lib/common/ @@ -13,4 +14,4 @@ devconfig/* # Build *.db checkVersion.sh -bun.lockb \ No newline at end of file +bun.lockb diff --git a/.vscode/settings.json b/.vscode/settings.json index bc189eaf..ca143742 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,6 @@ ".env.universal": ".env.*", "tsconfig.json": "tsconfig.*.json, env.d.ts, vite.config.ts", "package.json": "package-lock.json, eslint*, .prettier*, .editorconfig, manifest.json, logo.png, .gitignore, LICENSE" - } + }, + "css.customData": [".vscode/tailwindcss.json"], } \ No newline at end of file diff --git a/.vscode/tailwindcss.json b/.vscode/tailwindcss.json new file mode 100644 index 00000000..4c40326f --- /dev/null +++ b/.vscode/tailwindcss.json @@ -0,0 +1,55 @@ +{ + "version": 1.1, + "atDirectives": [ + { + "name": "@tailwind", + "description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.", + "references": [ + { + "name": "Tailwind Documentation", + "url": "https://tailwindcss.com/docs/functions-and-directives#tailwind" + } + ] + }, + { + "name": "@apply", + "description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.", + "references": [ + { + "name": "Tailwind Documentation", + "url": "https://tailwindcss.com/docs/functions-and-directives#apply" + } + ] + }, + { + "name": "@responsive", + "description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n", + "references": [ + { + "name": "Tailwind Documentation", + "url": "https://tailwindcss.com/docs/functions-and-directives#responsive" + } + ] + }, + { + "name": "@screen", + "description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n", + "references": [ + { + "name": "Tailwind Documentation", + "url": "https://tailwindcss.com/docs/functions-and-directives#screen" + } + ] + }, + { + "name": "@variants", + "description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n", + "references": [ + { + "name": "Tailwind Documentation", + "url": "https://tailwindcss.com/docs/functions-and-directives#variants" + } + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index ea0eaa54..3b8fc828 100644 --- a/README.md +++ b/README.md @@ -64,4 +64,4 @@ NapCat 在设计理念下遵守 OneBot 规范大多数要求并且积极改进 ## 开源附加 -任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经仓库主作者授权二次分发或基于 NapCat 代码开发。** +任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**本仓库仅用于提高易用性,实现消息推送类功能,此外,禁止任何项目未经仓库主作者授权基于 NapCat 代码开发。使用请遵守当地法律法规,由此造成的问题由使用者和提供违规使用教程者负责。** diff --git a/eslint.config.mjs b/eslint.config.mjs index adf72e5e..ae25a7a3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,70 +1,32 @@ -import typescriptEslint from "@typescript-eslint/eslint-plugin"; -import _import from "eslint-plugin-import"; -import { fixupPluginRules } from "@eslint/compat"; +import eslint from '@eslint/js'; +import tsEslintPlugin from '@typescript-eslint/eslint-plugin'; +import tsEslintParser from '@typescript-eslint/parser'; import globals from "globals"; -import tsParser from "@typescript-eslint/parser"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; -const filename = fileURLToPath(import.meta.url); -const dirname = path.dirname(filename); -const compat = new FlatCompat({ - baseDirectory: dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all -}); - -export default [{ - ignores: ["src/core/proto/"], -}, ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), { - plugins: { - "@typescript-eslint": typescriptEslint, - import: fixupPluginRules(_import), - }, - - languageOptions: { - globals: { - ...globals.browser, - ...globals.node, - }, - - parser: tsParser, - ecmaVersion: "latest", - sourceType: "module", - }, - - settings: { - "import/parsers": { - "@typescript-eslint/parser": [".ts"], - }, - - "import/resolver": { - typescript: { - alwaysTryTypes: true, +const customTsFlatConfig = [ + { + name: 'typescript-eslint/base', + languageOptions: { + parser: tsEslintParser, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + NodeJS: 'readonly', // 添加 NodeJS 全局变量 }, }, - }, - - rules: { - indent: ["error", 4], - semi: ["error", "always"], - "no-unused-vars": "off", - "no-async-promise-executor": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-var-requires": "off", - "object-curly-spacing": ["error", "always"], - }, -}, { - files: ["**/.eslintrc.{js,cjs}"], - - languageOptions: { - globals: { - ...globals.node, + files: ['**/*.{ts,tsx}'], + rules: { + ...tsEslintPlugin.configs.recommended.rules, + 'quotes': ['error', 'single'], // 使用单引号 + 'semi': ['error', 'always'], // 强制使用分号 + 'indent': ['error', 4], // 使用 4 空格缩进 }, - ecmaVersion: 5, - sourceType: "commonjs", + plugins: { + '@typescript-eslint': tsEslintPlugin, + }, + ignores: ['src/webui/**'], // 忽略 src/webui/ 目录所有文件 }, -}]; +]; + +export default [eslint.configs.recommended, ...customTsFlatConfig]; \ No newline at end of file diff --git a/manifest.json b/manifest.json index e13a0920..5aa0f42b 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.4.10", + "version": "4.5.23", "icon": "./logo.png", "authors": [ { diff --git a/napcat.webui/package.json b/napcat.webui/package.json index 1649d9dc..bd3c4239 100644 --- a/napcat.webui/package.json +++ b/napcat.webui/package.json @@ -4,14 +4,16 @@ "version": "0.0.6", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host=0.0.0.0", "build": "tsc && vite build", "lint": "eslint -c eslint.config.mjs ./src/**/**/*.{ts,tsx} --fix", "preview": "vite preview" }, "dependencies": { - "@monaco-editor/loader": "^1.4.0", - "@monaco-editor/react": "4.7.0-rc.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@heroui/accordion": "^2.2.8", "@heroui/avatar": "2.2.7", "@heroui/breadcrumbs": "2.2.7", "@heroui/button": "2.2.10", @@ -28,76 +30,88 @@ "@heroui/listbox": "2.3.10", "@heroui/modal": "2.2.8", "@heroui/navbar": "2.2.9", + "@heroui/pagination": "^2.2.9", "@heroui/popover": "2.3.10", "@heroui/select": "2.4.10", + "@heroui/skeleton": "^2.2.6", "@heroui/slider": "2.4.8", "@heroui/snippet": "2.2.11", "@heroui/spinner": "2.2.7", "@heroui/switch": "2.2.9", "@heroui/system": "2.4.7", + "@heroui/table": "^2.2.9", "@heroui/tabs": "2.2.8", "@heroui/theme": "2.4.6", "@heroui/tooltip": "2.2.8", - "@react-aria/visually-hidden": "3.8.18", - "@reduxjs/toolkit": "^2.5.0", + "@monaco-editor/loader": "^1.4.0", + "@monaco-editor/react": "4.7.0-rc.0", + "@react-aria/visually-hidden": "^3.8.19", + "@reduxjs/toolkit": "^2.5.1", "@uidotdev/usehooks": "^2.4.1", + "@xterm/addon-canvas": "^0.7.0", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", - "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", "ahooks": "^3.8.4", "axios": "^1.7.9", - "clsx": "2.1.1", + "clsx": "^2.1.1", "echarts": "^5.5.1", "event-source-polyfill": "^1.0.31", - "framer-motion": "^11.15.0", + "framer-motion": "^12.0.6", "monaco-editor": "^0.52.2", - "motion": "^11.15.0", + "motion": "^12.0.6", + "path-browserify": "^1.0.1", "qface": "^1.4.1", "qrcode.react": "^4.2.0", "quill": "^2.0.3", - "react": "19.0.0", - "react-dom": "19.0.0", + "react": "^19.0.0", + "react-color": "^2.19.3", + "react-dom": "^19.0.0", + "react-dropzone": "^14.3.5", "react-error-boundary": "^5.0.0", "react-hook-form": "^7.54.2", "react-hot-toast": "^2.4.1", "react-icons": "^5.4.0", + "react-markdown": "^9.0.3", + "react-photo-view": "^1.2.7", "react-redux": "^9.2.0", "react-responsive": "^10.0.0", - "react-router-dom": "7.1.0", + "react-router-dom": "^7.1.4", "react-use-websocket": "^4.11.1", "react-window": "^1.8.11", - "tailwind-variants": "0.3.0", - "tailwindcss": "3.4.17", + "remark-gfm": "^4.0.0", + "tailwind-variants": "^0.3.0", + "tailwindcss": "^3.4.17", "zod": "^3.24.1" }, "devDependencies": { - "@eslint/js": "^9.17.0", + "@eslint/js": "^9.19.0", "@react-types/shared": "^3.26.0", - "@trivago/prettier-plugin-sort-imports": "^5.2.0", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/event-source-polyfill": "^1.0.5", "@types/fabric": "^5.3.9", - "@types/node": "22.10.2", - "@types/react": "19.0.2", - "@types/react-dom": "19.0.2", + "@types/node": "^22.12.0", + "@types/path-browserify": "^1.0.3", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@types/react-window": "^1.8.8", - "@typescript-eslint/eslint-plugin": "8.18.1", - "@typescript-eslint/parser": "8.18.1", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "10.4.20", - "eslint": "^9.17.0", - "eslint-config-prettier": "9.1.0", + "autoprefixer": "^10.4.20", + "eslint": "^9.19.0", + "eslint-config-prettier": "^10.0.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-prettier": "5.2.3", "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-unused-imports": "4.1.4", + "eslint-plugin-unused-imports": "^4.1.4", "globals": "^15.14.0", - "postcss": "8.4.49", - "prettier": "3.4.2", - "typescript": "5.7.2", + "postcss": "^8.5.1", + "prettier": "^3.4.2", + "typescript": "^5.7.3", "vite": "^6.0.5", "vite-plugin-static-copy": "^2.2.0", "vite-tsconfig-paths": "^5.1.4" diff --git a/napcat.webui/public/fonts/AaCute.woff b/napcat.webui/public/fonts/AaCute.woff new file mode 100644 index 00000000..19fcf9b8 Binary files /dev/null and b/napcat.webui/public/fonts/AaCute.woff differ diff --git a/napcat.webui/public/fonts/FiraCode-VariableFont_wght.ttf b/napcat.webui/public/fonts/FiraCode-VariableFont_wght.ttf deleted file mode 100644 index 5655ed51..00000000 Binary files a/napcat.webui/public/fonts/FiraCode-VariableFont_wght.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/JetBrainsMono-Italic.ttf b/napcat.webui/public/fonts/JetBrainsMono-Italic.ttf new file mode 100644 index 00000000..54148355 Binary files /dev/null and b/napcat.webui/public/fonts/JetBrainsMono-Italic.ttf differ diff --git a/napcat.webui/public/fonts/JetBrainsMono.ttf b/napcat.webui/public/fonts/JetBrainsMono.ttf new file mode 100644 index 00000000..b60e77f5 Binary files /dev/null and b/napcat.webui/public/fonts/JetBrainsMono.ttf differ diff --git a/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Bold.ttf b/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Bold.ttf deleted file mode 100644 index f415f3e9..00000000 Binary files a/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Bold.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Italic.ttf b/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Italic.ttf deleted file mode 100644 index 152c1e37..00000000 Binary files a/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Italic.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Regular.ttf b/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Regular.ttf deleted file mode 100644 index 8b871395..00000000 Binary files a/napcat.webui/public/fonts/LibreBaskerville/LibreBaskerville-Regular.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/NotoSerifSC-VariableFont_wght.ttf b/napcat.webui/public/fonts/NotoSerifSC-VariableFont_wght.ttf deleted file mode 100644 index 6168dac0..00000000 Binary files a/napcat.webui/public/fonts/NotoSerifSC-VariableFont_wght.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/Outfit-VariableFont_wght.ttf b/napcat.webui/public/fonts/Outfit-VariableFont_wght.ttf deleted file mode 100644 index 96106f09..00000000 Binary files a/napcat.webui/public/fonts/Outfit-VariableFont_wght.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/harmony/HarmonyOS_Sans_SC_Bold.ttf b/napcat.webui/public/fonts/harmony/HarmonyOS_Sans_SC_Bold.ttf deleted file mode 100755 index 5c925d1f..00000000 Binary files a/napcat.webui/public/fonts/harmony/HarmonyOS_Sans_SC_Bold.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/harmony/HarmonyOS_Sans_SC_Regular.ttf b/napcat.webui/public/fonts/harmony/HarmonyOS_Sans_SC_Regular.ttf deleted file mode 100755 index aff150a1..00000000 Binary files a/napcat.webui/public/fonts/harmony/HarmonyOS_Sans_SC_Regular.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-Bold.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-Bold.ttf deleted file mode 100644 index c2293d5c..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-Bold.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-BoldItalic.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-BoldItalic.ttf deleted file mode 100644 index ce6e784d..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-BoldItalic.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-Italic.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-Italic.ttf deleted file mode 100644 index a599244e..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-Italic.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-Light.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-Light.ttf deleted file mode 100644 index b310d150..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-Light.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-LightItalic.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-LightItalic.ttf deleted file mode 100644 index ad0741b4..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-LightItalic.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-Medium.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-Medium.ttf deleted file mode 100644 index 7340a40a..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-Medium.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-MediumItalic.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-MediumItalic.ttf deleted file mode 100644 index 36ac1aed..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-MediumItalic.ttf and /dev/null differ diff --git a/napcat.webui/public/fonts/ubuntu/Ubuntu-Regular.ttf b/napcat.webui/public/fonts/ubuntu/Ubuntu-Regular.ttf deleted file mode 100644 index f98a2dab..00000000 Binary files a/napcat.webui/public/fonts/ubuntu/Ubuntu-Regular.ttf and /dev/null differ diff --git a/napcat.webui/src/App.tsx b/napcat.webui/src/App.tsx index 968ff6fe..9db7243d 100644 --- a/napcat.webui/src/App.tsx +++ b/napcat.webui/src/App.tsx @@ -16,6 +16,16 @@ import store from '@/store' const WebLoginPage = lazy(() => import('@/pages/web_login')) const IndexPage = lazy(() => import('@/pages/index')) const QQLoginPage = lazy(() => import('@/pages/qq_login')) +const DashboardIndexPage = lazy(() => import('@/pages/dashboard')) +const AboutPage = lazy(() => import('@/pages/dashboard/about')) +const ConfigPage = lazy(() => import('@/pages/dashboard/config')) +const DebugPage = lazy(() => import('@/pages/dashboard/debug')) +const HttpDebug = lazy(() => import('@/pages/dashboard/debug/http')) +const WSDebug = lazy(() => import('@/pages/dashboard/debug/websocket')) +const FileManagerPage = lazy(() => import('@/pages/dashboard/file_manager')) +const LogsPage = lazy(() => import('@/pages/dashboard/logs')) +const NetworkPage = lazy(() => import('@/pages/dashboard/network')) +const TerminalPage = lazy(() => import('@/pages/dashboard/terminal')) function App() { return ( @@ -58,9 +68,21 @@ function AuthChecker({ children }: { children: React.ReactNode }) { function AppRoutes() { return ( - } path="/*" /> - } path="/qq_login" /> - } path="/web_login" /> + }> + } /> + } /> + } /> + } /> + }> + } /> + } /> + + } /> + } /> + } /> + + } /> + } /> ) } diff --git a/napcat.webui/src/components/ColorPicker.tsx b/napcat.webui/src/components/ColorPicker.tsx new file mode 100644 index 00000000..eb9f423a --- /dev/null +++ b/napcat.webui/src/components/ColorPicker.tsx @@ -0,0 +1,36 @@ +import { Popover, PopoverContent, PopoverTrigger } from '@heroui/popover' +import React from 'react' +import { ColorResult, SketchPicker } from 'react-color' + +// 假定 heroui 提供的 Popover组件 + +interface ColorPickerProps { + color: string + onChange: (color: ColorResult) => void +} + +const ColorPicker: React.FC = ({ color, onChange }) => { + const handleChange = (colorResult: ColorResult) => { + onChange(colorResult) + } + + return ( + + +
+ + + + + + ) +} + +export default ColorPicker diff --git a/napcat.webui/src/components/audio_player.tsx b/napcat.webui/src/components/audio_player.tsx index 72d81374..f88b31af 100644 --- a/napcat.webui/src/components/audio_player.tsx +++ b/napcat.webui/src/components/audio_player.tsx @@ -187,7 +187,7 @@ export default function AudioPlayer(props: AudioPlayerProps) { return (
setIsCollapsed(!isCollapsed)} > diff --git a/napcat.webui/src/components/button/add_button.tsx b/napcat.webui/src/components/button/add_button.tsx index 1b3f8f70..571766e7 100644 --- a/napcat.webui/src/components/button/add_button.tsx +++ b/napcat.webui/src/components/button/add_button.tsx @@ -33,7 +33,7 @@ const AddButton: React.FC = (props) => { > - + {refresh && ( + + )}
) diff --git a/napcat.webui/src/components/chat_input/components/audio_insert.tsx b/napcat.webui/src/components/chat_input/components/audio_insert.tsx index 5e26587d..d94ba2cf 100644 --- a/napcat.webui/src/components/chat_input/components/audio_insert.tsx +++ b/napcat.webui/src/components/chat_input/components/audio_insert.tsx @@ -110,7 +110,7 @@ const AudioInsert = () => {
- @@ -120,7 +120,7 @@ const AudioInsert = () => { {showPreview && audioPreview && ( @@ -65,7 +65,7 @@ const EmojiPicker = ({ onInsertEmoji, onOpenChange }: EmojiPickerProps) => { {visibleEmojis.map((emoji) => ( @@ -45,7 +45,7 @@ const FileInsert = () => { @@ -33,7 +33,7 @@ const ImageInsert = ({ insertImage, onOpenChange }: ImageInsertProps) => { @@ -132,7 +132,7 @@ const MusicInsert = () => { @@ -38,7 +38,7 @@ const ReplyInsert = ({ insertReply }: ReplyInsertProps) => { }} /> @@ -45,7 +45,7 @@ const VideoInsert = () => { - diff --git a/napcat.webui/src/components/code_editor.tsx b/napcat.webui/src/components/code_editor.tsx index 9acb9f79..43af156f 100644 --- a/napcat.webui/src/components/code_editor.tsx +++ b/napcat.webui/src/components/code_editor.tsx @@ -19,12 +19,6 @@ loader.config({ } }) -loader.config({ - 'vs/nls': { - availableLanguages: { '*': 'zh-cn' } - } -}) - export interface CodeEditorProps extends React.ComponentProps { test?: string } diff --git a/napcat.webui/src/components/display_card/common_card.tsx b/napcat.webui/src/components/display_card/common_card.tsx index 860d781c..caf8a361 100644 --- a/napcat.webui/src/components/display_card/common_card.tsx +++ b/napcat.webui/src/components/display_card/common_card.tsx @@ -78,7 +78,7 @@ const NetworkDisplayCard = ({ {debug ? '关闭调试' : '开启调试'} + + + +
+ + + + + + + + ) +} diff --git a/napcat.webui/src/components/file_manage/file_edit_modal.tsx b/napcat.webui/src/components/file_manage/file_edit_modal.tsx new file mode 100644 index 00000000..cc906e63 --- /dev/null +++ b/napcat.webui/src/components/file_manage/file_edit_modal.tsx @@ -0,0 +1,94 @@ +import { Button } from '@heroui/button' +import { Code } from '@heroui/code' +import { + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader +} from '@heroui/modal' + +import CodeEditor from '@/components/code_editor' + +interface FileEditModalProps { + isOpen: boolean + file: { path: string; content: string } | null + onClose: () => void + onSave: () => void + onContentChange: (newContent?: string) => void +} + +export default function FileEditModal({ + isOpen, + file, + onClose, + onSave, + onContentChange +}: FileEditModalProps) { + // 根据文件后缀返回对应语言 + const getLanguage = (filePath: string) => { + if (filePath.endsWith('.js')) return 'javascript' + if (filePath.endsWith('.ts')) return 'typescript' + if (filePath.endsWith('.tsx')) return 'tsx' + if (filePath.endsWith('.jsx')) return 'jsx' + if (filePath.endsWith('.vue')) return 'vue' + if (filePath.endsWith('.svelte')) return 'svelte' + if (filePath.endsWith('.json')) return 'json' + if (filePath.endsWith('.html')) return 'html' + if (filePath.endsWith('.css')) return 'css' + if (filePath.endsWith('.scss')) return 'scss' + if (filePath.endsWith('.less')) return 'less' + if (filePath.endsWith('.md')) return 'markdown' + if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) return 'yaml' + if (filePath.endsWith('.xml')) return 'xml' + if (filePath.endsWith('.sql')) return 'sql' + if (filePath.endsWith('.sh')) return 'shell' + if (filePath.endsWith('.bat')) return 'bat' + if (filePath.endsWith('.php')) return 'php' + if (filePath.endsWith('.java')) return 'java' + if (filePath.endsWith('.c')) return 'c' + if (filePath.endsWith('.cpp')) return 'cpp' + if (filePath.endsWith('.h')) return 'h' + if (filePath.endsWith('.hpp')) return 'hpp' + if (filePath.endsWith('.go')) return 'go' + if (filePath.endsWith('.py')) return 'python' + if (filePath.endsWith('.rb')) return 'ruby' + if (filePath.endsWith('.cs')) return 'csharp' + if (filePath.endsWith('.swift')) return 'swift' + if (filePath.endsWith('.vb')) return 'vb' + if (filePath.endsWith('.lua')) return 'lua' + if (filePath.endsWith('.pl')) return 'perl' + if (filePath.endsWith('.r')) return 'r' + return 'plaintext' + } + + return ( + + + + 编辑文件 + {file?.path} + + +
+ +
+
+ + + + +
+
+ ) +} diff --git a/napcat.webui/src/components/file_manage/file_preview_modal.tsx b/napcat.webui/src/components/file_manage/file_preview_modal.tsx new file mode 100644 index 00000000..e416eb48 --- /dev/null +++ b/napcat.webui/src/components/file_manage/file_preview_modal.tsx @@ -0,0 +1,92 @@ +import { Button } from '@heroui/button' +import { + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader +} from '@heroui/modal' +import { Spinner } from '@heroui/spinner' +import { useRequest } from 'ahooks' +import path from 'path-browserify' +import { useEffect } from 'react' + +import FileManager from '@/controllers/file_manager' + +interface FilePreviewModalProps { + isOpen: boolean + filePath: string + onClose: () => void +} + +export const videoExts = ['.mp4', '.webm'] +export const audioExts = ['.mp3', '.wav'] + +export const supportedPreviewExts = [...videoExts, ...audioExts] + +export default function FilePreviewModal({ + isOpen, + filePath, + onClose +}: FilePreviewModalProps) { + const ext = path.extname(filePath).toLowerCase() + const { data, loading, error, run } = useRequest( + async () => FileManager.downloadToURL(filePath), + { + refreshDeps: [filePath], + manual: true, + refreshDepsAction: () => { + const ext = path.extname(filePath).toLowerCase() + if (!filePath || !supportedPreviewExts.includes(ext)) { + return + } + run() + } + } + ) + + useEffect(() => { + if (filePath) { + run() + } + }, [filePath]) + + let contentElement = null + if (!supportedPreviewExts.includes(ext)) { + contentElement =
暂不支持预览此文件类型
+ } else if (error) { + contentElement =
读取文件失败
+ } else if (loading || !data) { + contentElement = ( +
+ +
+ ) + } else if (videoExts.includes(ext)) { + contentElement =