diff --git a/components/__tests__/ToolCard.test.tsx b/components/__tests__/ToolCard.test.tsx index 63d3c83..01c83d6 100644 --- a/components/__tests__/ToolCard.test.tsx +++ b/components/__tests__/ToolCard.test.tsx @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { act, fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import ToolCard from '@/pages/Dashboard/ToolCard'; -import AccessTimeIcon from '@mui/icons-material/AccessTime'; +import { Clock } from 'lucide-react'; describe('ToolCard 组件', () => { beforeEach(() => { @@ -16,7 +16,7 @@ describe('ToolCard 组件', () => { title="测试工具" description="这是一个测试工具" colorKey="primary" - icon={AccessTimeIcon} + icon={Clock} onClick={() => {}} />, ); @@ -26,16 +26,14 @@ describe('ToolCard 组件', () => { }); it('无描述时仅渲染标题', () => { - render( - {}} />, - ); + render( {}} />); expect(screen.getByText('仅标题')).toBeInTheDocument(); }); it('应渲染图标', () => { const { container } = render( - {}} />, + {}} />, ); const svgElement = container.querySelector('svg'); @@ -47,7 +45,7 @@ describe('ToolCard 组件', () => { {}} snapshot={
快照内容
} />, @@ -58,16 +56,14 @@ describe('ToolCard 组件', () => { it('未提供快照时不渲染快照区域', () => { const { container } = render( - {}} />, + {}} />, ); expect(container.querySelector('[data-testid="snapshot"]')).not.toBeInTheDocument(); }); it('应使用 CardActionArea 渲染,支持键盘聚焦', () => { - render( - {}} />, - ); + render( {}} />); const button = screen.getByRole('button', { name: /可聚焦/ }); expect(button).toBeInTheDocument(); @@ -78,9 +74,7 @@ describe('ToolCard 组件', () => { describe('交互测试', () => { it('点击时应调用 onClick', () => { const handleClick = vi.fn(); - render( - , - ); + render(); const button = screen.getByRole('button', { name: /可点击/ }); fireEvent.click(button); @@ -90,14 +84,7 @@ describe('ToolCard 组件', () => { it('按 Enter 键时应调用 onClick', async () => { const handleClick = vi.fn(); - render( - , - ); + render(); const button = screen.getByRole('button', { name: /键盘可触发/ }); await act(async () => { @@ -112,7 +99,7 @@ describe('ToolCard 组件', () => { describe('样式测试', () => { it('应应用自定义颜色代码', () => { const { container } = render( - {}} />, + {}} />, ); const svgElement = container.querySelector('svg'); diff --git a/config/features.tsx b/config/features.tsx index 270c396..3f2def8 100644 --- a/config/features.tsx +++ b/config/features.tsx @@ -1,15 +1,17 @@ import { type ComponentType, lazy } from 'react'; -import type { SvgIconProps } from '@mui/material/SvgIcon'; +import type { LucideProps } from 'lucide-react'; import type { PageType } from '@/types/storage'; -import { Clock } from 'lucide-react'; -import StorageIcon from '@mui/icons-material/Storage'; -import QrCodeIcon from '@mui/icons-material/QrCode'; -import DescriptionIcon from '@mui/icons-material/Description'; -import VpnKeyIcon from '@mui/icons-material/VpnKey'; -import CompareArrowsIcon from '@mui/icons-material/CompareArrows'; -import TransformIcon from '@mui/icons-material/Transform'; -import CodeIcon from '@mui/icons-material/Code'; -import ArticleIcon from '@mui/icons-material/Article'; +import { + Clock, + Database, + QrCode, + FileText, + Key, + GitCompareArrows, + ArrowLeftRight, + Code, + File, +} from 'lucide-react'; export type PaletteColorKey = 'primary' | 'success' | 'warning' | 'error' | 'secondary' | 'info'; @@ -25,31 +27,16 @@ const Base64ConverterPage = lazy(() => import('@/pages/Base64Converter')); const MarkdownToHtmlPage = lazy(() => import('@/pages/MarkdownToHtml')); const HtmlToMarkdownPage = lazy(() => import('@/pages/HtmlToMarkdown')); -/** - * 功能配置接口 - * - * 整合了路由信息和仪表盘卡片元数据,作为功能的单一事实来源 - */ export interface FeatureConfig { - /** 页面类型标识 */ key: PageType; - /** 功能名称翻译键 */ labelKey: string; - /** 功能描述翻译键 */ descriptionKey: string; - /** 主题颜色键(用于仪表盘卡片,映射到 theme.palette[key].main) */ themeColorKey?: PaletteColorKey; - /** 图标组件引用(用于仪表盘卡片,按需实例化) */ - icon?: ComponentType; - /** 默认是否在仪表盘显示 */ + icon?: ComponentType; defaultVisible: boolean; - /** 不同显示模式对应的组件 */ components: { - /** 弹窗模式组件 */ popup: ComponentType; - /** 侧边栏模式组件 */ sidepanel: ComponentType; - /** 标签页模式组件 */ tab: ComponentType; }; } @@ -84,7 +71,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:storageCleaner.title', descriptionKey: 'features:storageCleaner.description', themeColorKey: 'warning', - icon: StorageIcon, + icon: Database, defaultVisible: true, components: { popup: StorageCleanerPage, @@ -97,7 +84,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:qrCode.title', descriptionKey: 'features:qrCode.description', themeColorKey: 'success', - icon: QrCodeIcon, + icon: QrCode, defaultVisible: true, components: { popup: QrCodePage, @@ -110,7 +97,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:textStatistics.title', descriptionKey: 'features:textStatistics.description', themeColorKey: 'secondary', - icon: DescriptionIcon, + icon: FileText, defaultVisible: true, components: { popup: TextStatisticsPage, @@ -123,7 +110,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:jwt.title', descriptionKey: 'features:jwt.description', themeColorKey: 'info', - icon: VpnKeyIcon, + icon: Key, defaultVisible: true, components: { popup: JwtPage, @@ -136,7 +123,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:jsonDiff.title', descriptionKey: 'features:jsonDiff.description', themeColorKey: 'primary', - icon: CompareArrowsIcon, + icon: GitCompareArrows, defaultVisible: true, components: { popup: JsonToolsPage, @@ -149,7 +136,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:base64Converter.title', descriptionKey: 'features:base64Converter.description', themeColorKey: 'info', - icon: TransformIcon, + icon: ArrowLeftRight, defaultVisible: true, components: { popup: Base64ConverterPage, @@ -162,7 +149,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:markdownToHtml.title', descriptionKey: 'features:markdownToHtml.description', themeColorKey: 'secondary', - icon: CodeIcon, + icon: Code, defaultVisible: true, components: { popup: MarkdownToHtmlPage, @@ -175,7 +162,7 @@ export const FEATURES: FeatureConfig[] = [ labelKey: 'features:htmlToMarkdown.title', descriptionKey: 'features:htmlToMarkdown.description', themeColorKey: 'secondary', - icon: ArticleIcon, + icon: File, defaultVisible: true, components: { popup: HtmlToMarkdownPage, diff --git a/entrypoints/options/App.tsx b/entrypoints/options/App.tsx index 4b15a30..9f9d8ba 100644 --- a/entrypoints/options/App.tsx +++ b/entrypoints/options/App.tsx @@ -1,20 +1,5 @@ import { SyntheticEvent, useEffect, useMemo, useState } from 'react'; -import { - alpha, - Box, - CircularProgress, - IconButton, - Paper, - Stack, - Switch, - Tab, - Tabs, - Tooltip, - Typography, -} from '@mui/material'; -import SettingsIcon from '@mui/icons-material/Settings'; -import RefreshIcon from '@mui/icons-material/Refresh'; -import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; +import { Settings, RefreshCw, GripVertical } from 'lucide-react'; import { DndContext, closestCenter, @@ -43,12 +28,19 @@ import { import GlobalSnackbar, { useSnackbarState } from '@/components/GlobalSnackbar'; import PageErrorBoundary from '@/components/PageErrorBoundary'; import PageHeader from '@/components/PageHeader'; -import { useTheme, type PaletteColor, type Theme } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; import type { PaletteColorKey } from '@/config/features'; -const getPaletteColor = (theme: Theme, key: PaletteColorKey): PaletteColor => - (theme.palette as unknown as Record)[key]; +const PALETTE_COLORS: Record = { + primary: '#1976d2', + success: '#2e7d32', + warning: '#e65100', + error: '#c62828', + secondary: '#9c27b0', + info: '#0288d1', +}; + +const getColorCode = (key: PaletteColorKey): string => PALETTE_COLORS[key]; const isValidPage = (page: unknown): page is PageType => { return typeof page === 'string' && (getAllFeatureKeys() as string[]).includes(page); @@ -75,7 +67,6 @@ function SortableFeatureRow({ isDisabled, onToggle, }: SortableFeatureRowProps) { - const theme = useTheme(); const { t } = useTranslation(['features']); const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: pageKey, @@ -85,131 +76,85 @@ function SortableFeatureRow({ if (!feature) return null; const colorKey = feature.themeColorKey ?? 'primary'; - const colorCode = getPaletteColor(theme, colorKey).main; + const colorCode = getColorCode(colorKey); const style = { transform: CSS.Transform.toString(transform), transition, zIndex: isDragging ? 1 : 'auto', position: 'relative' as const, + backgroundColor: isDragging ? `${colorCode}0a` : 'transparent', + boxShadow: isDragging ? '0 8px 20px rgba(0,0,0,0.08)' : 'none', }; return ( - - - {/* 拖拽手柄 - 整行可拖,手柄是视觉暗示 */} - + {/* 拖拽手柄 */} +
- - + +
{/* 功能图标 */} - - {feature.icon && } - + {feature.icon && } + {/* 文本信息 */} - - +
+
{t(feature.labelKey)} - +
{feature.descriptionKey && ( - - - {t(feature.descriptionKey)} - - +
+ {t(feature.descriptionKey)} +
)} - - +
+ - onToggle(pageKey)} + {/* 开关 */} + + ); } -/** - * Options 设置页面主组件 - * 支持对不同窗口入口的功能显示和排序进行独立配置 - */ export default function App() { - const theme = useTheme(); const { t } = useTranslation(['features', 'common']); const initialWindowType = useMemo(() => { @@ -360,120 +305,64 @@ export default function App() { if (!isLoaded) { return ( - - - +
+
+
); } return ( - +
{/* 顶部标题与导航栏 */} - - +
+
} - iconColor={theme.palette.primary.main} + icon={} + iconColor="#1976d2" title="应用设置" subtitle="针对不同窗口类型独立配置 Dashboard 中显示的功能及其排序" sx={{ marginBottom: '1rem' }} /> {/* Tab 与恢复按钮同行 */} - - +
+ {(['popup', 'sidepanel', 'tab'] as WindowType[]).map((type) => ( + + ))} +
+ +
+
+
{/* 主内容区域 */} - - - +
+
+
- +
{pageOrder.map((key, index, array) => { const isChecked = visiblePages.includes(key); const isDisabled = isChecked && visiblePages.length === 1; @@ -488,15 +377,15 @@ export default function App() { /> ); })} - +
- - - +
+
+
-
+
); } diff --git a/package-lock.json b/package-lock.json index 0a4ef24..29f0ae4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,9 @@ "@dnd-kit/utilities": "^3.2.2", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@mui/icons-material": "^7.3.8", - "@mui/material": "^7.3.8", + "@mui/icons-material": "^9.0.1", + "@mui/material": "^9.0.1", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-slot": "^1.2.4", "@testing-library/dom": "^10.4.1", "@vitest/coverage-v8": "^4.1.7", @@ -1762,9 +1763,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "7.3.11", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.11.tgz", - "integrity": "sha512-a7I/b/nBTdXYz2cOSlEmkQ9WWE1x8FHpqMhFPp+Y1VPFxcOw91G5ELOHARQAGSPy5V+UCgJua6K/1x70bAtQPw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.0.1.tgz", + "integrity": "sha512-GzamIIhZ1bH77dq7eKaeyRgJdkypsxin4jBFq2EMs4lBWRR0LFO1CSVMsoebn/VvjcNrnrOrjy48MkrkQUK2iw==", "license": "MIT", "funding": { "type": "opencollective", @@ -1772,12 +1773,12 @@ } }, "node_modules/@mui/icons-material": { - "version": "7.3.11", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.11.tgz", - "integrity": "sha512-+hz5ilwHZ3djd5es3sCErLioqe/NhZcYTsV/TNXZAMdJdb23F4xzJjqnnZdnurc3S1+ietcssRNqieOhPQLZ7Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-9.0.1.tgz", + "integrity": "sha512-5PRpQjVLTNLyV/2J9J53Yz4R0tVbodG0BQDN2zQI1QBG1OPYM25ar+4N20eyFOfJT6zKglLzsnU70+zdVLaTkw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6" + "@babel/runtime": "^7.29.2" }, "engines": { "node": ">=14.0.0" @@ -1787,7 +1788,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^7.3.11", + "@mui/material": "^9.0.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1798,22 +1799,22 @@ } }, "node_modules/@mui/material": { - "version": "7.3.11", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.11.tgz", - "integrity": "sha512-yq8bPc3LxOwKRWpcjRgDkYFmpM6aKlARfESTmOQcvLYFeJwtHte2tw6hJDrb8sk8wcvpDprHEHVaoUU0MslIkw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.0.1.tgz", + "integrity": "sha512-voyCpeUxcSWLN7KPZuq0pGCIt726T9K6kiVM3XUcywZDAlZSarLHaUxJVQpospbjjOzN53hwyjo8s6KoWl6utw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/core-downloads-tracker": "^7.3.11", - "@mui/system": "^7.3.11", - "@mui/types": "^7.4.12", - "@mui/utils": "^7.3.11", + "@babel/runtime": "^7.29.2", + "@mui/core-downloads-tracker": "^9.0.1", + "@mui/system": "^9.0.1", + "@mui/types": "^9.0.0", + "@mui/utils": "^9.0.1", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.2.3", "prop-types": "^15.8.1", - "react-is": "^19.2.3", + "react-is": "^19.2.4", "react-transition-group": "^4.4.5" }, "engines": { @@ -1826,7 +1827,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^7.3.11", + "@mui/material-pigment-css": "^9.0.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1847,13 +1848,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "7.3.11", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.11.tgz", - "integrity": "sha512-9B+YKms0fRHbNrqp9tOT/DNbNnU5gyvJ1o3qAGXfq8GmZcbJnE3At9x07Zr/o0pkhzg4aDdwXVqe4+AcgtOCPA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.0.1.tgz", + "integrity": "sha512-pSIGq4Yw749KHEwlkYZWVERgHgwJELP6ODtBNUfV8V4oIb5H+h7IQDFXuk/b2oQccODK1enJAtiEzlgLZmq+8g==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/utils": "^7.3.11", + "@babel/runtime": "^7.29.2", + "@mui/utils": "^9.0.1", "prop-types": "^15.8.1" }, "engines": { @@ -1874,12 +1875,12 @@ } }, "node_modules/@mui/styled-engine": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.10.tgz", - "integrity": "sha512-WxE9SiF8xskAQqGjsp0poXCkCqsoXFEsSr0HBXfApmGHR+DBnXRp+z46Vsltg4gpPM4Z96DeAQRpeAOnhNg7Ng==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.0.0.tgz", + "integrity": "sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", + "@babel/runtime": "^7.29.2", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", @@ -1908,16 +1909,16 @@ } }, "node_modules/@mui/system": { - "version": "7.3.11", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.11.tgz", - "integrity": "sha512-7izwGWdNawAKpBKcRlx7f2gFnAAjmASBWvMcyX4YYEeLOFsbfGRbUYGInvnAcUeql3rPxI7F9Ft4oY2OLRz44g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.0.1.tgz", + "integrity": "sha512-WvlioaLxk6ewUIOfh0StxUvOPDS1mCfzaulcudsL1brZNXuh0N9FMk7RpH7ImJKjEz412SEy/V/yvqmtxbqxCQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/private-theming": "^7.3.11", - "@mui/styled-engine": "^7.3.10", - "@mui/types": "^7.4.12", - "@mui/utils": "^7.3.11", + "@babel/runtime": "^7.29.2", + "@mui/private-theming": "^9.0.1", + "@mui/styled-engine": "^9.0.0", + "@mui/types": "^9.0.0", + "@mui/utils": "^9.0.1", "clsx": "^2.1.1", "csstype": "^3.2.3", "prop-types": "^15.8.1" @@ -1948,12 +1949,12 @@ } }, "node_modules/@mui/types": { - "version": "7.4.12", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz", - "integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-9.0.0.tgz", + "integrity": "sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6" + "@babel/runtime": "^7.29.2" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1965,17 +1966,17 @@ } }, "node_modules/@mui/utils": { - "version": "7.3.11", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.11.tgz", - "integrity": "sha512-XTjGnifwteg71/ij+0e7Y7d+hwyntMYP5wPoA/g2drdGH+Flkvjwy0OfrVpKBbaOvofq4zU/LIyUZyKgmWu18g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.1.tgz", + "integrity": "sha512-f3UO3jNN1pYg5zxqXC81Bvv8hx5ACcYc0387382ZI7M5ono1heIwHYLrKsz85myguWdeVKPRZGmDdynWUBjK2g==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/types": "^7.4.12", + "@babel/runtime": "^7.29.2", + "@mui/types": "^9.0.0", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^19.2.3" + "react-is": "^19.2.4" }, "engines": { "node": ">=14.0.0" @@ -2114,6 +2115,12 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", @@ -2129,6 +2136,249 @@ } } }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", @@ -2147,6 +2397,91 @@ } } }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", @@ -2635,7 +2970,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -3333,6 +3668,18 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -4750,6 +5097,12 @@ "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -6061,6 +6414,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-port-please": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", @@ -9600,6 +9962,75 @@ "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", "license": "MIT" }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -11414,6 +11845,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", diff --git a/package.json b/package.json index 8fcc43c..b431b5f 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,9 @@ "@dnd-kit/utilities": "^3.2.2", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@mui/icons-material": "^7.3.8", - "@mui/material": "^7.3.8", + "@mui/icons-material": "^9.0.1", + "@mui/material": "^9.0.1", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-slot": "^1.2.4", "@testing-library/dom": "^10.4.1", "@vitest/coverage-v8": "^4.1.7", diff --git a/pages/Dashboard/ToolCard.tsx b/pages/Dashboard/ToolCard.tsx index e8c37f2..a8e0329 100644 --- a/pages/Dashboard/ToolCard.tsx +++ b/pages/Dashboard/ToolCard.tsx @@ -1,12 +1,6 @@ -/** - * ToolCard 组件 - 工具卡片 - * - * 用于在仪表盘中展示各个工具功能的卡片组件,支持图标、标题、描述、 - * 快照内容展示,具备悬停动画效果。 - */ import { ChevronRight } from 'lucide-react'; import type { ComponentType } from 'react'; -import type { SvgIconProps } from '@mui/material/SvgIcon'; +import type { LucideProps } from 'lucide-react'; import type { PaletteColorKey } from '@/config/features'; const PALETTE_COLORS: Record = { @@ -23,7 +17,7 @@ interface ToolCardProps { description?: string; snapshot?: React.ReactNode; colorKey: PaletteColorKey; - icon: ComponentType; + icon: ComponentType; onClick: () => void; } @@ -68,7 +62,7 @@ export default function ToolCard({ color: colorCode, }} > - +
diff --git a/pages/HtmlToMarkdown/index.tsx b/pages/HtmlToMarkdown/index.tsx index 5ca614d..5656818 100644 --- a/pages/HtmlToMarkdown/index.tsx +++ b/pages/HtmlToMarkdown/index.tsx @@ -1,18 +1,5 @@ import { useCallback, useMemo, useState } from 'react'; -import { - Alert, - alpha, - Box, - Button, - Container, - Stack, - TextField, - Typography, - Paper, -} from '@mui/material'; -import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; -import DownloadIcon from '@mui/icons-material/Download'; -import CodeIcon from '@mui/icons-material/Code'; +import { Code, Download, Trash2 } from 'lucide-react'; import { useLazyTranslation } from '@/utils/useLazyTranslation'; import PageHeader from '@/components/PageHeader'; import CopyButton from '@/components/CopyButton'; @@ -57,18 +44,12 @@ export default function HtmlToMarkdownPage() { const showOutput = previewMode !== 'markdown'; return ( - - } /> +
+ } /> - +
{/* 工具栏 */} - +
- - - + - - + +
+
{/* 错误提示 */} {error && ( - +
{error} - +
)} {/* 主内容区 */} - {/* HTML 输入区 */} {showInput && ( - - alpha(theme.palette.primary.main, 0.04), - borderBottom: '1px solid', - borderColor: 'divider', - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }} - > - - {t('inputLabel')} - - +
+
+ {t('inputLabel')} + {t('charCount', { count: html.length })} - - - +
+