From 84f0e0f9a0b2c8c978d82bbd04f972a339b5a55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 22 Dec 2025 12:27:56 +0800 Subject: [PATCH] Refactor UI for network cards and improve theming Redesigned network display cards and related components for a more modern, consistent look, including improved button styles, card layouts, and responsive design. Added support for background images and dynamic theming across cards, tables, and log views. Enhanced input and select components with unified styling. Improved file table responsiveness and log display usability. Refactored OneBot API debug and navigation UI for better usability and mobile support. --- .../src/components/button/add_button.tsx | 6 +- .../src/components/button/save_buttons.tsx | 22 +- .../src/components/chat_input/modal.tsx | 11 +- .../src/components/code_editor.tsx | 11 +- .../components/display_card/common_card.tsx | 176 +++++++++---- .../src/components/display_card/container.tsx | 44 ++-- .../components/display_card/http_client.tsx | 12 +- .../components/display_card/http_server.tsx | 12 +- .../display_card/http_sse_server.tsx | 12 +- .../src/components/display_card/ws_client.tsx | 12 +- .../src/components/display_card/ws_server.tsx | 12 +- .../src/components/display_network_item.tsx | 10 +- .../src/components/file_manage/file_table.tsx | 12 +- .../src/components/hitokoto.tsx | 65 +++-- .../src/components/input/file_input.tsx | 9 +- .../src/components/input/image_input.tsx | 11 +- .../src/components/log_com/history.tsx | 42 ++-- .../components/log_com/log_level_select.tsx | 18 +- .../src/components/log_com/realtime.tsx | 12 +- .../components/network_edit/generic_form.tsx | 9 + .../src/components/onebot/api/debug.tsx | 80 +++--- .../src/components/onebot/api/nav_list.tsx | 168 ++++++++----- .../src/components/qq_info_card.tsx | 27 +- .../src/components/sidebar/index.tsx | 9 +- .../src/components/switch_card.tsx | 22 +- .../src/components/system_info.tsx | 44 +++- .../src/components/system_status_display.tsx | 49 +++- .../src/components/xterm.tsx | 18 +- .../src/pages/dashboard/config/index.tsx | 74 +++--- .../src/pages/dashboard/config/onebot.tsx | 5 + .../src/pages/dashboard/config/server.tsx | 63 ++--- .../src/pages/dashboard/config/webui.tsx | 23 +- .../src/pages/dashboard/debug/http/index.tsx | 62 ++--- .../pages/dashboard/debug/websocket/index.tsx | 4 +- .../src/pages/dashboard/file_manager.tsx | 238 ++++++++++-------- .../src/pages/dashboard/index.tsx | 11 +- .../src/pages/dashboard/network.tsx | 3 +- .../src/pages/dashboard/terminal.tsx | 66 ++--- 38 files changed, 919 insertions(+), 565 deletions(-) diff --git a/packages/napcat-webui-frontend/src/components/button/add_button.tsx b/packages/napcat-webui-frontend/src/components/button/add_button.tsx index 3461cc5e..f6b230b7 100644 --- a/packages/napcat-webui-frontend/src/components/button/add_button.tsx +++ b/packages/napcat-webui-frontend/src/components/button/add_button.tsx @@ -18,7 +18,7 @@ import { } from '../icons'; export interface AddButtonProps { - onOpen: (key: keyof OneBotConfig['network']) => void + onOpen: (key: keyof OneBotConfig['network']) => void; } const AddButton: React.FC = (props) => { @@ -33,7 +33,7 @@ const AddButton: React.FC = (props) => { > )} diff --git a/packages/napcat-webui-frontend/src/components/chat_input/modal.tsx b/packages/napcat-webui-frontend/src/components/chat_input/modal.tsx index 841c9258..a693de2e 100644 --- a/packages/napcat-webui-frontend/src/components/chat_input/modal.tsx +++ b/packages/napcat-webui-frontend/src/components/chat_input/modal.tsx @@ -15,8 +15,15 @@ export default function ChatInputModal () { return ( <> - { - test?: string + test?: string; } export type CodeEditorRef = monaco.editor.IStandaloneCodeEditor; diff --git a/packages/napcat-webui-frontend/src/components/display_card/common_card.tsx b/packages/napcat-webui-frontend/src/components/display_card/common_card.tsx index b920198d..439f7f7c 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/common_card.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/common_card.tsx @@ -1,5 +1,6 @@ -import { Button, ButtonGroup } from '@heroui/button'; +import { Button } from '@heroui/button'; import { Switch } from '@heroui/switch'; +import clsx from 'clsx'; import { useState } from 'react'; import { CgDebug } from 'react-icons/cg'; import { FiEdit3 } from 'react-icons/fi'; @@ -10,27 +11,25 @@ import DisplayCardContainer from './container'; type NetworkType = OneBotConfig['network']; export type NetworkDisplayCardFields = Array<{ - label: string - value: NetworkType[T][0][keyof NetworkType[T][0]] + label: string; + value: NetworkType[T][0][keyof NetworkType[T][0]]; render?: ( value: NetworkType[T][0][keyof NetworkType[T][0]] - ) => React.ReactNode + ) => React.ReactNode; }>; export interface NetworkDisplayCardProps { - data: NetworkType[T][0] - showType?: boolean - typeLabel: string - fields: NetworkDisplayCardFields - onEdit: () => void - onEnable: () => Promise - onDelete: () => Promise - onEnableDebug: () => Promise + data: NetworkType[T][0]; + typeLabel: string; + fields: NetworkDisplayCardFields; + onEdit: () => void; + onEnable: () => Promise; + onDelete: () => Promise; + onEnableDebug: () => Promise; } -const NetworkDisplayCard = ({ +const NetworkDisplayCard = ({ data, - showType, typeLabel, fields, onEdit, @@ -56,79 +55,146 @@ const NetworkDisplayCard = ({ onEnableDebug().finally(() => setEditing(false)); }; + const isFullWidthField = (label: string) => ['URL', 'Token', 'AccessToken'].includes(label); + return ( +
- +
} enableSwitch={ } - tag={showType && typeLabel} - title={name} + title={typeLabel} > -
- {fields.map((field, index) => ( -
- {field.label} - {field.render - ? ( - field.render(field.value) - ) - : ( - {field.value} - )} -
- ))} +
+ {(() => { + const targetFullField = fields.find(f => isFullWidthField(f.label)); + + if (targetFullField) { + // 模式1:存在全宽字段(如URL),布局为: + // Row 1: 名称 (全宽) + // Row 2: 全宽字段 (全宽) + return ( + <> +
+ 名称 +
+ {name} +
+
+
+ {targetFullField.label} +
+ {targetFullField.render + ? targetFullField.render(targetFullField.value) + : ( + + {String(targetFullField.value)} + + )} +
+
+ + ); + } else { + // 模式2:无全宽字段,布局为 4 个小块 (2行 x 2列) + // Row 1: 名称 | Field 0 + // Row 2: Field 1 | Field 2 + const displayFields = fields.slice(0, 3); + return ( + <> +
+ 名称 +
+ {name} +
+
+ {displayFields.map((field, index) => ( +
+ {field.label} +
+ {field.render + ? ( + field.render(field.value) + ) + : ( + + {String(field.value)} + + )} +
+
+ ))} + {/* 如果字段不足3个,可以补充空白块占位吗?或者是让它空着?用户说要高度一致。只要是grid,通常高度会被撑开。目前这样应该能保证最多2行。 */} + + ); + } + })()}
); diff --git a/packages/napcat-webui-frontend/src/components/display_card/container.tsx b/packages/napcat-webui-frontend/src/components/display_card/container.tsx index c96c9f8a..e3a1fd0d 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/container.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/container.tsx @@ -1,7 +1,8 @@ import { Card, CardBody, CardFooter, CardHeader } from '@heroui/card'; +import { useLocalStorage } from '@uidotdev/usehooks'; import clsx from 'clsx'; +import key from '@/const/key'; -import { title } from '../primitives'; export interface ContainerProps { title: string; @@ -9,6 +10,7 @@ export interface ContainerProps { action: React.ReactNode; enableSwitch: React.ReactNode; children: React.ReactNode; + className?: string; // Add className prop } export interface DisplayCardProps { @@ -25,31 +27,35 @@ const DisplayCardContainer: React.FC = ({ tag, enableSwitch, children, + className, }) => { + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; + return ( - - + + {tag && ( -
+
{tag}
)} -

- {_title} -

-
{enableSwitch}
+
+
+ + {_title} + +
+
+
{enableSwitch}
- {children} - {action} + {children} + {action} ); }; diff --git a/packages/napcat-webui-frontend/src/components/display_card/http_client.tsx b/packages/napcat-webui-frontend/src/components/display_card/http_client.tsx index 9519f269..b9f3ce20 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/http_client.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/http_client.tsx @@ -4,12 +4,12 @@ import NetworkDisplayCard from './common_card'; import type { NetworkDisplayCardFields } from './common_card'; interface HTTPClientDisplayCardProps { - data: OneBotConfig['network']['httpClients'][0] - showType?: boolean - onEdit: () => void - onEnable: () => Promise - onDelete: () => Promise - onEnableDebug: () => Promise + data: OneBotConfig['network']['httpClients'][0]; + showType?: boolean; + onEdit: () => void; + onEnable: () => Promise; + onDelete: () => Promise; + onEnableDebug: () => Promise; } const HTTPClientDisplayCard: React.FC = (props) => { diff --git a/packages/napcat-webui-frontend/src/components/display_card/http_server.tsx b/packages/napcat-webui-frontend/src/components/display_card/http_server.tsx index a72c4692..1ba55318 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/http_server.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/http_server.tsx @@ -4,12 +4,12 @@ import NetworkDisplayCard from './common_card'; import type { NetworkDisplayCardFields } from './common_card'; interface HTTPServerDisplayCardProps { - data: OneBotConfig['network']['httpServers'][0] - showType?: boolean - onEdit: () => void - onEnable: () => Promise - onDelete: () => Promise - onEnableDebug: () => Promise + data: OneBotConfig['network']['httpServers'][0]; + showType?: boolean; + onEdit: () => void; + onEnable: () => Promise; + onDelete: () => Promise; + onEnableDebug: () => Promise; } const HTTPServerDisplayCard: React.FC = (props) => { diff --git a/packages/napcat-webui-frontend/src/components/display_card/http_sse_server.tsx b/packages/napcat-webui-frontend/src/components/display_card/http_sse_server.tsx index f6ea34f3..38e3e6bc 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/http_sse_server.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/http_sse_server.tsx @@ -4,12 +4,12 @@ import NetworkDisplayCard from './common_card'; import type { NetworkDisplayCardFields } from './common_card'; interface HTTPSSEServerDisplayCardProps { - data: OneBotConfig['network']['httpSseServers'][0] - showType?: boolean - onEdit: () => void - onEnable: () => Promise - onDelete: () => Promise - onEnableDebug: () => Promise + data: OneBotConfig['network']['httpSseServers'][0]; + showType?: boolean; + onEdit: () => void; + onEnable: () => Promise; + onDelete: () => Promise; + onEnableDebug: () => Promise; } const HTTPSSEServerDisplayCard: React.FC = ( diff --git a/packages/napcat-webui-frontend/src/components/display_card/ws_client.tsx b/packages/napcat-webui-frontend/src/components/display_card/ws_client.tsx index deba3d30..b69c57ea 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/ws_client.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/ws_client.tsx @@ -4,12 +4,12 @@ import NetworkDisplayCard from './common_card'; import type { NetworkDisplayCardFields } from './common_card'; interface WebsocketClientDisplayCardProps { - data: OneBotConfig['network']['websocketClients'][0] - showType?: boolean - onEdit: () => void - onEnable: () => Promise - onDelete: () => Promise - onEnableDebug: () => Promise + data: OneBotConfig['network']['websocketClients'][0]; + showType?: boolean; + onEdit: () => void; + onEnable: () => Promise; + onDelete: () => Promise; + onEnableDebug: () => Promise; } const WebsocketClientDisplayCard: React.FC = ( diff --git a/packages/napcat-webui-frontend/src/components/display_card/ws_server.tsx b/packages/napcat-webui-frontend/src/components/display_card/ws_server.tsx index 1f1e9ece..4c345def 100644 --- a/packages/napcat-webui-frontend/src/components/display_card/ws_server.tsx +++ b/packages/napcat-webui-frontend/src/components/display_card/ws_server.tsx @@ -4,12 +4,12 @@ import NetworkDisplayCard from './common_card'; import type { NetworkDisplayCardFields } from './common_card'; interface WebsocketServerDisplayCardProps { - data: OneBotConfig['network']['websocketServers'][0] - showType?: boolean - onEdit: () => void - onEnable: () => Promise - onDelete: () => Promise - onEnableDebug: () => Promise + data: OneBotConfig['network']['websocketServers'][0]; + showType?: boolean; + onEdit: () => void; + onEnable: () => Promise; + onDelete: () => Promise; + onEnableDebug: () => Promise; } const WebsocketServerDisplayCard: React.FC = ( diff --git a/packages/napcat-webui-frontend/src/components/display_network_item.tsx b/packages/napcat-webui-frontend/src/components/display_network_item.tsx index 322977b3..2ad197d5 100644 --- a/packages/napcat-webui-frontend/src/components/display_network_item.tsx +++ b/packages/napcat-webui-frontend/src/components/display_network_item.tsx @@ -1,5 +1,7 @@ import { Card, CardBody } from '@heroui/card'; +import { useLocalStorage } from '@uidotdev/usehooks'; import clsx from 'clsx'; +import key from '@/const/key'; @@ -14,10 +16,16 @@ const NetworkItemDisplay: React.FC = ({ label, size = 'md', }) => { + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; + return ( 名称 - + 类型 - + 大小 - + 修改时间 操作 @@ -194,13 +194,13 @@ export default function FileTable ({ )} - {file.isDirectory ? '目录' : '文件'} - + {file.isDirectory ? '目录' : '文件'} + {isNaN(file.size) || file.isDirectory ? '-' : `${file.size} 字节`} - {new Date(file.mtime).toLocaleString()} + {new Date(file.mtime).toLocaleString()}
- -
- ))} -
- + + + )} + + +
+ } + value={searchValue} + onChange={(e) => setSearchValue(e.target.value)} + onClear={() => setSearchValue('')} + size="sm" + /> +
+ + + {Object.entries(data).map(([apiName, api]) => { + const isMatch = apiName.toLowerCase().includes(searchValue.toLowerCase()) || + api.description?.toLowerCase().includes(searchValue.toLowerCase()); + if (!isMatch) return null; + + const isSelected = apiName === selectedApi; + + return ( +
onSelect(apiName as OneBotHttpApiPath)} + onKeyDown={(e) => e.key === 'Enter' && onSelect(apiName as OneBotHttpApiPath)} + className="cursor-pointer focus:outline-none" + > + + +
+ + {api.description} + + + {apiName} + +
+
+
+
+ ); + })} +
+ + + ); }; diff --git a/packages/napcat-webui-frontend/src/components/qq_info_card.tsx b/packages/napcat-webui-frontend/src/components/qq_info_card.tsx index eb5e2bd2..3bb53cd6 100644 --- a/packages/napcat-webui-frontend/src/components/qq_info_card.tsx +++ b/packages/napcat-webui-frontend/src/components/qq_info_card.tsx @@ -1,8 +1,10 @@ import { Card, CardBody } from '@heroui/card'; import { Image } from '@heroui/image'; +import { useLocalStorage } from '@uidotdev/usehooks'; import clsx from 'clsx'; import { BsTencentQq } from 'react-icons/bs'; +import key from '@/const/key'; import { SelfInfo } from '@/types/user'; import PageLoading from './page_loading'; @@ -14,9 +16,14 @@ export interface QQInfoCardProps { } const QQInfoCard: React.FC = ({ data, error, loading }) => { + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; return ( @@ -32,9 +39,11 @@ const QQInfoCard: React.FC = ({ data, error, loading }) => { ) : ( -
- -
+ {!hasBackground && ( +
+ +
+ )}
= ({ data, error, loading }) => { />
-
+
{data?.nick || '未知用户'}
-
+
{data?.uin || 'Unknown'}
diff --git a/packages/napcat-webui-frontend/src/components/sidebar/index.tsx b/packages/napcat-webui-frontend/src/components/sidebar/index.tsx index 28a419a7..6d7932ed 100644 --- a/packages/napcat-webui-frontend/src/components/sidebar/index.tsx +++ b/packages/napcat-webui-frontend/src/components/sidebar/index.tsx @@ -1,15 +1,17 @@ import { Button } from '@heroui/button'; +import { useLocalStorage } from '@uidotdev/usehooks'; import clsx from 'clsx'; import { AnimatePresence, motion } from 'motion/react'; import React from 'react'; import { IoMdLogOut } from 'react-icons/io'; import { MdDarkMode, MdLightMode } from 'react-icons/md'; +import key from '@/const/key'; import useAuth from '@/hooks/auth'; import useDialog from '@/hooks/use-dialog'; import { useTheme } from '@/hooks/use-theme'; - import type { MenuItem } from '@/config/site'; + import Menus from './menus'; interface SideBarProps { @@ -22,6 +24,7 @@ const SideBar: React.FC = (props) => { const { open, items, onClose } = props; const { toggleTheme, isDark } = useTheme(); const { revokeAuth } = useAuth(); + const [b64img] = useLocalStorage(key.backgroundImage, ''); const dialog = useDialog(); const onRevokeAuth = () => { dialog.confirm({ @@ -47,7 +50,9 @@ const SideBar: React.FC = (props) => { void - name?: string - onBlur?: React.FocusEventHandler - disabled?: boolean - onChange?: React.ChangeEventHandler + label?: string; + description?: string; + value?: boolean; + onValueChange?: (value: boolean) => void; + name?: string; + onBlur?: React.FocusEventHandler; + disabled?: boolean; + onChange?: React.ChangeEventHandler; } const SwitchCard = forwardRef( @@ -22,9 +22,9 @@ const SwitchCard = forwardRef( = ({ @@ -28,12 +30,21 @@ const SystemInfoItem: React.FC = ({ value = '--', icon, endContent, + hasBackground = false, }) => { return ( -
+
{icon}
{title}
-
{value}
+
{value}
{endContent}
); @@ -261,7 +272,11 @@ const NewVersionTip = (props: NewVersionTipProps) => { ); }; -const NapCatVersion = () => { +interface NapCatVersionProps { + hasBackground?: boolean; +} + +const NapCatVersion: React.FC = ({ hasBackground = false }) => { const { data: packageData, loading: packageLoading, @@ -274,6 +289,7 @@ const NapCatVersion = () => { } + hasBackground={hasBackground} value={ packageError ? ( @@ -302,18 +318,28 @@ const SystemInfo: React.FC = (props) => { loading: qqVersionLoading, error: qqVersionError, } = useRequest(WebUIManager.getQQVersion); + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; + return ( - - + + 系统信息
- + } + hasBackground={hasBackground} value={ qqVersionError ? ( @@ -332,11 +358,13 @@ const SystemInfo: React.FC = (props) => { title='WebUI 版本' icon={} value='Next' + hasBackground={hasBackground} /> } value={archInfo} + hasBackground={hasBackground} />
diff --git a/packages/napcat-webui-frontend/src/components/system_status_display.tsx b/packages/napcat-webui-frontend/src/components/system_status_display.tsx index d80afb05..037b1886 100644 --- a/packages/napcat-webui-frontend/src/components/system_status_display.tsx +++ b/packages/napcat-webui-frontend/src/components/system_status_display.tsx @@ -1,10 +1,12 @@ import { Card, CardBody } from '@heroui/card'; import { Image } from '@heroui/image'; +import { useLocalStorage } from '@uidotdev/usehooks'; import clsx from 'clsx'; import { BiSolidMemoryCard } from 'react-icons/bi'; import { GiCpu } from 'react-icons/gi'; import bkg from '@/assets/images/bg/1AD934174C0107F14BAD8776D29C5F90.png'; +import key from '@/const/key'; import UsagePie from './usage_pie'; @@ -13,6 +15,7 @@ export interface SystemStatusItemProps { value?: string | number; size?: 'md' | 'lg'; unit?: string; + hasBackground?: boolean; } const SystemStatusItem: React.FC = ({ @@ -20,16 +23,26 @@ const SystemStatusItem: React.FC = ({ value = '-', size = 'md', unit, + hasBackground = false, }) => { return (
-
{title}
-
+
{title}
+
{value} {unit && {unit}}
@@ -53,9 +66,14 @@ const SystemStatusDisplay: React.FC = ({ data }) => { memoryUsage.system = (systemUsage / system) * 100; memoryUsage.qq = (qqUsage / system) * 100; } + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; return ( - +
= ({ data }) => {
-

+

CPU

- - - + + +
-

+

内存

@@ -98,16 +124,19 @@ const SystemStatusDisplay: React.FC = ({ data }) => { value={data?.memory.total} size='lg' unit='MB' + hasBackground={hasBackground} />
diff --git a/packages/napcat-webui-frontend/src/components/xterm.tsx b/packages/napcat-webui-frontend/src/components/xterm.tsx index 6cb1b2fc..7fb3eb6a 100644 --- a/packages/napcat-webui-frontend/src/components/xterm.tsx +++ b/packages/napcat-webui-frontend/src/components/xterm.tsx @@ -12,21 +12,21 @@ import { useTheme } from '@/hooks/use-theme'; export type XTermRef = { write: ( ...args: Parameters - ) => ReturnType - writeAsync: (data: Parameters[0]) => Promise + ) => ReturnType; + writeAsync: (data: Parameters[0]) => Promise; writeln: ( ...args: Parameters - ) => ReturnType - writelnAsync: (data: Parameters[0]) => Promise - clear: () => void - terminalRef: React.RefObject + ) => ReturnType; + writelnAsync: (data: Parameters[0]) => Promise; + clear: () => void; + terminalRef: React.RefObject; }; export interface XTermProps extends Omit, 'onInput' | 'onResize'> { - onInput?: (data: string) => void - onKey?: (key: string, event: KeyboardEvent) => void - onResize?: (cols: number, rows: number) => void // 新增属性 + onInput?: (data: string) => void; + onKey?: (key: string, event: KeyboardEvent) => void; + onResize?: (cols: number, rows: number) => void; // 新增属性 } const XTerm = forwardRef((props, ref) => { diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/config/index.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/config/index.tsx index f7bed85a..149f2d2a 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/config/index.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/config/index.tsx @@ -1,9 +1,11 @@ import { Card, CardBody } from '@heroui/card'; import { Tab, Tabs } from '@heroui/tabs'; +import { useLocalStorage } from '@uidotdev/usehooks'; import clsx from 'clsx'; -import { useMediaQuery } from 'react-responsive'; import { useNavigate, useSearchParams } from 'react-router-dom'; +import key from '@/const/key'; + import ChangePasswordCard from './change_password'; import LoginConfigCard from './login'; import OneBotConfigCard from './onebot'; @@ -12,24 +14,29 @@ import ThemeConfigCard from './theme'; import WebUIConfigCard from './webui'; export interface ConfigPageProps { - children?: React.ReactNode - size?: 'sm' | 'md' | 'lg' + children?: React.ReactNode; + size?: 'sm' | 'md' | 'lg'; } -const ConfingPageItem: React.FC = ({ +const ConfigPageItem: React.FC = ({ children, size = 'md', }) => { + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; + return ( - - -
+ + +
{children}
@@ -38,7 +45,6 @@ const ConfingPageItem: React.FC = ({ }; export default function ConfigPage () { - const isMediumUp = useMediaQuery({ minWidth: 768 }); const navigate = useNavigate(); const search = useSearchParams({ tab: 'onebot', @@ -46,53 +52,55 @@ export default function ConfigPage () { const tab = search.get('tab') ?? 'onebot'; return ( -
+
+ 其它配置 - NapCat WebUI { navigate(`/config?tab=${key}`); }} classNames={{ - tabList: 'sticky flex top-14 bg-opacity-50 backdrop-blur-sm', - panel: 'w-full relative', - base: 'md:!w-auto flex-grow-0 flex-shrink-0 mr-0', - cursor: 'bg-opacity-60 backdrop-blur-sm', + base: 'w-full flex-col items-center', + tabList: 'bg-white/40 dark:bg-black/20 backdrop-blur-md rounded-2xl p-1.5 shadow-sm border border-white/20 dark:border-white/5 mb-4 md:mb-8 w-full md:w-fit mx-auto overflow-x-auto hide-scrollbar', + cursor: 'bg-white/80 dark:bg-white/10 backdrop-blur-md shadow-sm rounded-xl', + tab: 'h-9 px-4 md:px-6', + tabContent: 'text-default-600 dark:text-default-300 font-medium group-data-[selected=true]:text-primary', + panel: 'w-full relative p-0', }} > - + - + - + - + - + - + - + - + - + - + - + - +
diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/config/onebot.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/config/onebot.tsx index f6423fc2..6c9c4551 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/config/onebot.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/config/onebot.tsx @@ -74,6 +74,11 @@ const OneBotConfigCard = () => { {...field} label='音乐签名地址' placeholder='请输入音乐签名地址' + classNames={{ + inputWrapper: + 'bg-default-100/50 dark:bg-white/5 backdrop-blur-md border border-transparent hover:bg-default-200/50 dark:hover:bg-white/10 transition-all shadow-sm data-[hover=true]:border-default-300', + input: 'bg-transparent text-default-700 placeholder:text-default-400', + }} /> )} /> diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/config/server.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/config/server.tsx index 0d00c325..b1a6e20d 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/config/server.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/config/server.tsx @@ -1,5 +1,4 @@ import { Input } from '@heroui/input'; -import { Switch } from '@heroui/switch'; import { useRequest } from 'ahooks'; import { useEffect } from 'react'; import { Controller, useForm } from 'react-hook-form'; @@ -7,6 +6,7 @@ import toast from 'react-hot-toast'; import SaveButtons from '@/components/button/save_buttons'; import PageLoading from '@/components/page_loading'; +import SwitchCard from '@/components/switch_card'; import WebUIManager from '@/controllers/webui_manager'; @@ -79,8 +79,8 @@ const ServerConfigCard = () => { <> 服务器配置 - NapCat WebUI
-
-
服务器配置
+
+
服务器配置
{ description='服务器监听的IP地址,0.0.0.0表示监听所有网卡' isDisabled={!!configError} errorMessage={configError ? '获取配置失败' : undefined} + classNames={{ + inputWrapper: + 'bg-default-100/50 dark:bg-white/5 backdrop-blur-md border border-transparent hover:bg-default-200/50 dark:hover:bg-white/10 transition-all shadow-sm data-[hover=true]:border-default-300', + input: 'bg-transparent text-default-700 placeholder:text-default-400', + }} /> )} /> @@ -109,6 +114,11 @@ const ServerConfigCard = () => { isDisabled={!!configError} errorMessage={configError ? '获取配置失败' : undefined} onChange={(e) => field.onChange(parseInt(e.target.value) || 0)} + classNames={{ + inputWrapper: + 'bg-default-100/50 dark:bg-white/5 backdrop-blur-md border border-transparent hover:bg-default-200/50 dark:hover:bg-white/10 transition-all shadow-sm data-[hover=true]:border-default-300', + input: 'bg-transparent text-gray-800 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-500', + }} /> )} /> @@ -126,47 +136,42 @@ const ServerConfigCard = () => { isDisabled={!!configError} errorMessage={configError ? '获取配置失败' : undefined} onChange={(e) => field.onChange(parseInt(e.target.value) || 0)} + classNames={{ + inputWrapper: + 'bg-default-100/50 dark:bg-white/5 backdrop-blur-md border border-transparent hover:bg-default-200/50 dark:hover:bg-white/10 transition-all shadow-sm data-[hover=true]:border-default-300', + input: 'bg-transparent text-gray-800 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-500', + }} /> )} />
-
-
安全配置
+
+
安全配置
( - field.onChange(value)} - isDisabled={!!configError} - > -
- 禁用WebUI - - 启用后将完全禁用WebUI服务,需要重启生效 - -
-
+ field.onChange(value)} + disabled={!!configError} + label='禁用WebUI' + description='启用后将完全禁用WebUI服务,需要重启生效' + /> )} /> ( - field.onChange(value)} - isDisabled={!!configError} - > -
- 禁用非局域网访问 - - 启用后只允许局域网内的设备访问WebUI,提高安全性 - -
-
+ field.onChange(value)} + disabled={!!configError} + label='禁用非局域网访问' + description='启用后只允许局域网内的设备访问WebUI,提高安全性' + /> )} />
diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/config/webui.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/config/webui.tsx index 8feaabe9..af18425f 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/config/webui.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/config/webui.tsx @@ -93,11 +93,13 @@ const WebUIConfigCard = () => { <> WebUI配置 - NapCat WebUI
-
WebUI字体
+
WebUI字体
此项不需要手动保存,上传成功后需清空网页缓存并刷新 { try { await FileManager.uploadWebUIFont(file); @@ -124,26 +126,35 @@ const WebUIConfigCard = () => {
-
背景图
+
背景图
} + render={({ field }) => ( + + )} />
-
自定义图标
+
自定义图标
{siteConfig.navItems.map((item) => ( } + render={({ field }) => ( + + )} /> ))}
-
Passkey认证
+
Passkey认证
注册Passkey后,您可以更便捷地登录WebUI,无需每次输入token
diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/debug/http/index.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/debug/http/index.tsx index 4163a97a..2418a245 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/debug/http/index.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/debug/http/index.tsx @@ -1,6 +1,5 @@ import { Button } from '@heroui/button'; import clsx from 'clsx'; -import { motion } from 'motion/react'; import { useEffect, useRef, useState } from 'react'; import { TbSquareRoundedChevronLeftFilled } from 'react-icons/tb'; @@ -27,36 +26,39 @@ export default function HttpDebug () { return ( <> HTTP调试 - NapCat WebUI - -
- + { + setSelectedApi(api); + // Auto-close sidebar on mobile after selection + if (window.innerWidth < 768) { + setOpenSideBar(false); + } + }} + openSideBar={openSideBar} + onToggle={setOpenSideBar} + /> +
- - - + {/* Toggle Button Container - positioned on top-left of content if sidebar is closed */} +
+ +
+ + +
); diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/debug/websocket/index.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/debug/websocket/index.tsx index f75c53ea..f9a0e942 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/debug/websocket/index.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/debug/websocket/index.tsx @@ -48,8 +48,8 @@ export default function WSDebug () { return ( <> Websocket调试 - NapCat WebUI -
- +
+
(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; + return ( -
-
- +
+
+
+ - + - - + + - {((selectedFiles instanceof Set && selectedFiles.size > 0) || - selectedFiles === 'all') && ( - <> - - + + + + )} +
+ +
+ + {currentPath.split('/').map((part, index, parts) => ( + { - setMoveTargetPath(''); - setIsMoveModalOpen(true); + const newPath = parts.slice(0, index + 1).join('/'); + navigate(`/file_manager#${encodeURIComponent(newPath)}`); }} - className='text-sm' - startContent={} > - ( - {selectedFiles instanceof Set ? selectedFiles.size : files.length} - ) - - - - )} - - {currentPath.split('/').map((part, index, parts) => ( - { - const newPath = parts.slice(0, index + 1).join('/'); - navigate(`/file_manager#${encodeURIComponent(newPath)}`); - }} - > - {part} - - ))} - - setJumpPath(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter' && jumpPath.trim() !== '') { - navigate(`/file_manager#${encodeURIComponent(jumpPath.trim())}`); - } - }} - className='ml-auto w-64' - /> + {part} + + ))} + + setJumpPath(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && jumpPath.trim() !== '') { + navigate(`/file_manager#${encodeURIComponent(jumpPath.trim())}`); + } + }} + className='w-full md:w-64' + classNames={{ + inputWrapper: 'bg-white/40 dark:bg-black/20 backdrop-blur-md', + }} + /> +
= ({ setArchInfo }) => { const DashboardIndexPage: React.FC = () => { const [archInfo, setArchInfo] = useState(); + // @ts-ignore + const [backgroundImage] = useLocalStorage(key.backgroundImage, ''); + const hasBackground = !!backgroundImage; return ( <> @@ -105,7 +111,10 @@ const DashboardIndexPage: React.FC = () => {
- + diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/network.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/network.tsx index 5aa4ea93..f6d93253 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/network.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/network.tsx @@ -375,9 +375,8 @@ export default function NetworkPage () { - - ))} - - + {tab.title} + + + ))} + + + )}