From ca4bfc33fd5f9a11a8fdfd90eac38bce976780de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Thu, 28 May 2026 20:57:08 +0800 Subject: [PATCH] refactor: clean up unused code and simplify imports - Remove unused imports and variables across 35 files - Simplify component logic and remove dead code - Clean up test files by removing unnecessary setup - Streamline CI workflow configuration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 11 ----------- eslint.config.ts | 15 +-------------- src/components/RouterContainer.tsx | 16 ++++------------ src/components/SwitchButtonGroup.tsx | 6 ++---- src/components/TextInputArea.tsx | 5 +---- src/components/TopBar.tsx | 8 +------- src/components/__tests__/ImageUploader.test.tsx | 3 --- src/components/__tests__/QrCodePreview.test.tsx | 2 -- .../__tests__/StorageCleanerConfirm.test.tsx | 4 ---- .../__tests__/SwitchButtonGroup.test.tsx | 3 --- src/components/__tests__/TextInputArea.test.tsx | 1 - src/components/__tests__/TopBar.test.tsx | 1 - src/entrypoints/background.ts | 6 ------ src/entrypoints/content/uiPopover.ts | 2 +- src/pages/Base64Converter/TextMode.tsx | 3 --- src/pages/Base64Converter/index.tsx | 2 +- src/pages/JsonTools/DiffResult.tsx | 2 -- src/pages/JsonTools/JsonConvertSection.tsx | 8 +------- src/pages/JsonTools/index.tsx | 4 ++-- src/pages/QrCode/__tests__/index.test.tsx | 5 ----- src/pages/StorageCleaner/StorageOptionsGrid.tsx | 4 +--- src/pages/StorageCleaner/index.tsx | 2 +- src/pages/StorageCleaner/useStorageCleaner.ts | 2 +- src/pages/TextStatistics/index.tsx | 1 - src/pages/Timestamp/LiveClock.tsx | 4 +--- src/pages/Timestamp/ResultView.tsx | 8 +++----- src/pages/Timestamp/index.tsx | 2 +- src/pages/Timestamp/useTimestampConverter.ts | 2 +- src/providers/RouterProvider.tsx | 2 -- src/providers/__tests__/RouterProvider.test.tsx | 8 -------- src/utils/__tests__/jwt.test.ts | 12 ------------ src/utils/__tests__/useStorageState.test.ts | 5 ++--- src/utils/chromeTabs.ts | 4 ---- src/utils/qrCodeParser.ts | 4 ---- src/utils/storageCleaner.ts | 5 +---- 35 files changed, 26 insertions(+), 146 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae16e93..c494b8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,6 @@ concurrency: cancel-in-progress: true jobs: - # 💡 1. 提速核心:前置基建节点(Infrastructure Initialization) - # 专门负责锁死环境、同步下载并缓存 node_modules,下游节点直接满血复用! setup: name: Prepare Dependencies runs-on: ubuntu-latest @@ -29,7 +27,6 @@ jobs: with: node-version: '22' - # 建立基于 package-lock.json 唯一哈希的缓存大闸 - name: Cache Node Modules id: cache-nodemodules uses: actions/cache@v4 @@ -47,7 +44,6 @@ jobs: id: cache-info run: echo "key=${{ runner.os }}-node-v22-${{ hashFiles('**/package-lock.json') }}" >> $GITHUB_OUTPUT - # 💡 2. 静态语法质检节点(依赖前置节点完成) lint: name: Lint runs-on: ubuntu-latest @@ -70,7 +66,6 @@ jobs: - name: Run ESLint run: npm run lint - # 💡 3. 强类型守卫节点(2秒瞬时恢复,开箱即查) typecheck: name: TypeScript Check runs-on: ubuntu-latest @@ -96,7 +91,6 @@ jobs: - name: Run TypeScript type check run: npm run typecheck - # 💡 4. 单元测试节点(无缝运行你刚刚修复完的 setupTests.ts 套件) test: name: Unit Tests runs-on: ubuntu-latest @@ -122,16 +116,12 @@ jobs: - name: Run tests run: npm run test - # 💡 5. 多端分布式最终编译节点(Production Matrix Compliance) build: name: Build (${{ matrix.browser }}) runs-on: ubuntu-latest - # 只有当 Linter、类型大闸、Vitest 单元测试全数满分通过,才放行最终打包编译 needs: [ lint, typecheck, test ] strategy: matrix: - # 💡 完美对齐 WXT 跨端架构:将 firefox 同步纳入生产编译大矩阵, - # 如果 firefox 编译因任何多端不兼容挂掉,CI 会立刻拉起警报,防护力拉满! browser: [ chrome, firefox ] fail-fast: false steps: @@ -149,7 +139,6 @@ jobs: path: node_modules key: ${{ runner.os }}-node-v22-${{ hashFiles('**/package-lock.json') }} - # 💡 动态代理编译指令:完美匹配 WXT / 各类多端打包器的标准构建命令 - name: Generate WXT types run: npx wxt prepare diff --git a/eslint.config.ts b/eslint.config.ts index fd0db65..28b8cab 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -5,16 +5,13 @@ import reactPlugin from 'eslint-plugin-react'; import globals from 'globals'; export default tseslint.config( - // 1. 全局物理隔离:彻底掐灭对构建产物与配置本身的干扰 { ignores: ['dist', '.output', '.wxt', 'node_modules', 'eslint.config.ts', 'eslint.config.js'], }, - // 2. 注入 JavaScript 与 TypeScript 的官方大师级推荐规则集 js.configs.recommended, ...tseslint.configs.recommended, - // 3. 针对测试文件专属沙箱:解耦强类型死锁,放行 any,容忍未消费变量 { files: ['**/__tests__/**', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}', 'setupTests.ts'], rules: { @@ -26,7 +23,6 @@ export default tseslint.config( }, }, - // 4. 核心业务全受控大管线(Hooks, Entrypoints, Components 统一护航) { files: ['src/**/*.{ts,tsx}'], ignores: ['**/__tests__/**', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'], @@ -37,17 +33,13 @@ export default tseslint.config( ...globals.browser, ...globals.node, }, - // 💡 修复点 1(史诗级治愈):废除脆弱的 project 硬编码路径! - // 拥抱 typescript-eslint 官方推荐的 projectService 常驻动态类型调度中枢。 - // 它会在内存中全自动、流式为所有新建、悬空或暂存文件分配编译上下文, - // 彻底终结 "file is not included in any tsconfig" 的全量崩溃黑洞! + parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, - // 挂载插件沙箱 plugins: { react: reactPlugin, 'react-hooks': reactHooks, @@ -59,24 +51,19 @@ export default tseslint.config( }, }, - // 💡 修复点 2:高精对齐 React 19 / JSX Runtime 的全量生产质检规则大闸 rules: { - // 激活 react-hooks 官方推荐规则 ...reactHooks.configs.recommended.rules, - // 激活 react 官方精选规则(排除旧版 React 必须手动 import 的历史包袱) ...reactPlugin.configs.recommended.rules, ...reactPlugin.configs['jsx-runtime'].rules, 'react/prop-types': 'off', - // 清洗原生未消费变量冲突,统一交由 TS 高阶哨兵接管 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': [ 'warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], - // 彻底关闭老旧的 JSX 作用域检查,全面契合 React 19 核心美学 'react/react-in-jsx-scope': 'off', }, }, diff --git a/src/components/RouterContainer.tsx b/src/components/RouterContainer.tsx index 41bd145..179ebf8 100644 --- a/src/components/RouterContainer.tsx +++ b/src/components/RouterContainer.tsx @@ -4,35 +4,32 @@ import { Suspense, useMemo } from 'react'; import { useI18n } from '@/utils/chromeI18n'; import PageErrorBoundary from '@/components/PageErrorBoundary'; import PageSkeleton from '@/components/PageSkeleton'; -import { cn } from '@/lib/utils'; // 1. 引入标准的 shadcn 工具函数 -import { AlertTriangle } from 'lucide-react'; // 用于标准的 404 异常展示 +import { cn } from '@/lib/utils'; +import { AlertTriangle } from 'lucide-react'; export default function RouterContainer() { const { currentPage, isLoaded } = useRouter(); const { t } = useI18n('common'); - // 2. 稳定的动态动画类名映射 const animationClass = useMemo(() => { return currentPage === 'dashboard' ? 'page-transition-dashboard' : 'page-transition-enter'; }, [currentPage]); const entryPointType = getEntryPointType(); - // 骨架屏加载状态守卫 if (!isLoaded) { return ; } - // 3. 严格的路由查找与类型安全的组件分发 const currentFeature = FEATURES.find((f) => f.key === currentPage); const MatchedComponent = currentFeature?.components?.[entryPointType]; return (
@@ -40,11 +37,6 @@ export default function RouterContainer() { fallback={} > - {/* - 4. 路由防御拦截: - 如果组件存在则正常流式渲染,如果由于版本更迭或非法路径导致找不到对应组件, - 渲染一个优雅且符合 shadcn 风格的中性 404 提示页,而不是死白屏。 - */} {MatchedComponent ? ( ) : ( diff --git a/src/components/SwitchButtonGroup.tsx b/src/components/SwitchButtonGroup.tsx index aee922f..77368d1 100644 --- a/src/components/SwitchButtonGroup.tsx +++ b/src/components/SwitchButtonGroup.tsx @@ -1,12 +1,11 @@ import React from 'react'; -import { cn } from '@/lib/utils'; // 1. 引入标准的 shadcn 工具函数 +import { cn } from '@/lib/utils'; export interface SwitchOption { value: T; label: React.ReactNode; } -// 2. 移除内联 sx,继承标准 HTML 属性,并使用标准的类名注入机制 export interface SwitchButtonGroupProps extends Omit< React.HTMLAttributes, 'onChange' @@ -15,7 +14,7 @@ export interface SwitchButtonGroupProps exte options: SwitchOption[]; onChange: (value: T) => void; size?: 'small' | 'medium' | 'large'; - buttonClassName?: string; // 替换原有的 buttonSx + buttonClassName?: string; } export default function SwitchButtonGroup({ @@ -27,7 +26,6 @@ export default function SwitchButtonGroup({ buttonClassName, ...props }: SwitchButtonGroupProps) { - // 3. 将尺寸和高度、内边距等整体对齐,保证按钮和背景容器成比例缩放 const sizeClasses = { small: 'text-xs h-8 px-2 py-1 rounded-md', medium: 'text-sm h-9 px-3 py-1.5 rounded-md', diff --git a/src/components/TextInputArea.tsx b/src/components/TextInputArea.tsx index 06666c7..fa79fab 100644 --- a/src/components/TextInputArea.tsx +++ b/src/components/TextInputArea.tsx @@ -121,10 +121,8 @@ const TextInputArea = forwardRef((props const value = isControlled ? controlledValue : internalValue; const displayError = externalError ?? error; - // 双向合并 ref 指针 useImperativeHandle(ref, () => internalRef.current as HTMLTextAreaElement); - // 1. 高性能的动态高度自适应计算 const adjustHeight = useCallback(() => { const textArea = internalRef.current; if (!textArea) return; @@ -132,14 +130,13 @@ const TextInputArea = forwardRef((props // 重置高度计算 textArea.style.height = 'auto'; - const computedMin = minRows * 24; // 每行粗略按 24px 计算 + const computedMin = minRows * 24; const computedMax = maxRows * 24; const nextHeight = Math.max(textArea.scrollHeight, computedMin); textArea.style.height = `${Math.min(nextHeight, computedMax)}px`; }, [minRows, maxRows]); - // 当数值改变时自适应扩展 React.useEffect(() => { adjustHeight(); }, [value, adjustHeight]); diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index dcf1e6e..e9cdba9 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -16,9 +16,8 @@ import { FeatureConfig, FEATURES } from '@/config/features'; import { storageUtil } from '@/utils/chromeStorage'; import { openExtensionPage } from '@/utils/chromeTabs'; import { useI18n } from '@/utils/chromeI18n'; -import { cn } from '@/lib/utils'; // 1. 引入 shadcn 核心工具函数 +import { cn } from '@/lib/utils'; -// 常量配置抽取(无需写在全局变量或 styles 对象里) const SEARCH_HISTORY_LIMIT = 10; const SEARCH_HISTORY_DISPLAY = 5; @@ -40,7 +39,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void }) window.close(); }; - // 2. 健壮的 Click Outside 逻辑:点击空白处收起搜索框 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { @@ -51,7 +49,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void }) return () => document.removeEventListener('mousedown', handleClickOutside); }, []); - // 从 Chrome Storage 异步初始化历史记录 useEffect(() => { storageUtil .get('app/searchHistory', []) @@ -61,7 +58,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void }) .catch((err) => console.error('加载搜索历史失败:', err)); }, []); - // 3. 模糊搜索匹配(移除了无意义的 dashboard 干扰项) const searchResults = useMemo(() => { const query = searchQuery.trim().toLowerCase(); if (!query) return []; @@ -79,7 +75,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void }) return searchHistory.slice(0, SEARCH_HISTORY_DISPLAY); }, [searchHistory, searchQuery]); - // 新增/持久化历史记录 const saveToHistory = async (query: string) => { if (!query.trim()) return; const nextHistory = [query, ...searchHistory.filter((h) => h !== query)].slice( @@ -104,7 +99,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void }) const ThemeIcon = mode === 'light' ? Sun : mode === 'dark' ? Moon : Monitor; - // 4. 健壮的键盘导航交互 const handleKeyDown = (e: React.KeyboardEvent) => { const totalItems = searchQuery.trim() ? searchResults.length : displayedHistory.length; diff --git a/src/components/__tests__/ImageUploader.test.tsx b/src/components/__tests__/ImageUploader.test.tsx index f98bcea..fcf48c0 100644 --- a/src/components/__tests__/ImageUploader.test.tsx +++ b/src/components/__tests__/ImageUploader.test.tsx @@ -13,7 +13,6 @@ const mockRevokeObjectURL = vi.fn(); Object.defineProperty(window.URL, 'createObjectURL', { value: mockCreateObjectURL }); Object.defineProperty(window.URL, 'revokeObjectURL', { value: mockRevokeObjectURL }); -// 模拟 showMessage vi.mock('@/components/GlobalSnackbar', () => ({ useSnackbar: () => ({ showMessage: vi.fn(), @@ -49,7 +48,6 @@ describe('ImageUploader 组件', () => { describe('渲染测试', () => { it('当没有选中文件时应显示上传提示', () => { render(); - // 💡 修复点 2:全面切换为高弹性正则,斩断双重命名空间死锁! expect(screen.getByText(/点击.*拖拽/)).toBeInTheDocument(); expect(screen.getByText(/格式/)).toBeInTheDocument(); }); @@ -65,7 +63,6 @@ describe('ImageUploader 组件', () => { , ); expect(screen.getByText('test.png')).toBeInTheDocument(); - // 💡 修复点 3(自愈第 62 行崩溃位置):利用正则模糊命中,彻底通过! expect(screen.getByText(/点击更换/)).toBeInTheDocument(); }); diff --git a/src/components/__tests__/QrCodePreview.test.tsx b/src/components/__tests__/QrCodePreview.test.tsx index ed5251c..2c6c8fc 100644 --- a/src/components/__tests__/QrCodePreview.test.tsx +++ b/src/components/__tests__/QrCodePreview.test.tsx @@ -18,7 +18,6 @@ describe('QrCodePreview 组件', () => { describe('渲染测试', () => { it('当 qrCodeDataUrl 为空时应显示占位文本', () => { render(); - // 💡 修复点 2:全面拥抱柔性正则匹配,直接终结多层 'qrCode:qrCode:' 前缀踩踏! expect(screen.getByText(/二维码将显示/)).toBeInTheDocument(); }); @@ -44,7 +43,6 @@ describe('QrCodePreview 组件', () => { onCopy={mockOnCopy} />, ); - // 💡 修复点 3:切换为正则,无缝过检 expect(screen.getByText(/下载二维码/)).toBeInTheDocument(); }); diff --git a/src/components/__tests__/StorageCleanerConfirm.test.tsx b/src/components/__tests__/StorageCleanerConfirm.test.tsx index b529a11..3fd0bc7 100644 --- a/src/components/__tests__/StorageCleanerConfirm.test.tsx +++ b/src/components/__tests__/StorageCleanerConfirm.test.tsx @@ -4,7 +4,6 @@ import { StorageCleanerConfirm } from '@/pages/StorageCleaner/StorageCleanerConf import type { StorageCleanerOptions } from '@/types/storage'; import React from 'react'; -// 💡 1. 核心超进化(WXT 规范):将全局多端 browser 桩进行全量注入与防干涉净化 const storageOnChangedMock = { addListener: vi.fn(), removeListener: vi.fn() }; (globalThis as any).chrome = { storage: { onChanged: storageOnChangedMock } }; (globalThis as any).browser = { storage: { onChanged: storageOnChangedMock } }; @@ -41,8 +40,6 @@ describe('StorageCleanerConfirm 组件', () => { describe('渲染测试', () => { it('open 为 true 时应渲染对话框', () => { renderComponent(); - // 💡 修复点 3:拥抱模糊正则断言。 - // 彻底终结由于 i18n 桩引起的 'storageCleaner:storageCleaner:' 双重前缀硬编码堆叠,100% 自愈放行! expect(screen.getByRole('heading', { name: /确认清理/ })).toBeInTheDocument(); }); @@ -60,7 +57,6 @@ describe('StorageCleanerConfirm 组件', () => { it('应显示取消和确认按钮', () => { renderComponent(); - // 💡 修复点 4:按钮的 Accessible Name 匹配同步切回高弹性正则模式,抵抗一切国际化双前缀污染 expect(screen.getByRole('button', { name: /取消/ })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /确认清理/ })).toBeInTheDocument(); }); diff --git a/src/components/__tests__/SwitchButtonGroup.test.tsx b/src/components/__tests__/SwitchButtonGroup.test.tsx index cc108ac..de0492b 100644 --- a/src/components/__tests__/SwitchButtonGroup.test.tsx +++ b/src/components/__tests__/SwitchButtonGroup.test.tsx @@ -21,9 +21,7 @@ describe('SwitchButtonGroup 组件', () => { const buttonA = screen.getByRole('button', { name: /选项A/i }); const buttonB = screen.getByRole('button', { name: /选项B/i }); - // 选中的按钮有 bg-background text-foreground shadow-sm 类 expect(buttonA).toHaveClass('bg-background', 'text-foreground', 'shadow-sm'); - // 未选中的按钮有 hover:bg-background/50 类 expect(buttonB).toHaveClass('hover:bg-background/50'); }); @@ -41,7 +39,6 @@ describe('SwitchButtonGroup 组件', () => { render(); fireEvent.click(screen.getByRole('button', { name: /选项A/i })); - // 新组件每次点击都会触发 onChange expect(handleChange).toHaveBeenCalledWith('a'); }); diff --git a/src/components/__tests__/TextInputArea.test.tsx b/src/components/__tests__/TextInputArea.test.tsx index 9603148..1e10a8e 100644 --- a/src/components/__tests__/TextInputArea.test.tsx +++ b/src/components/__tests__/TextInputArea.test.tsx @@ -353,7 +353,6 @@ describe('TextInputArea 组件', () => { it('readOnly 时输入框应只读', () => { render( {}} readOnly />); - // MUI TextField 的 readOnly 通过 inputProps 设置,textarea 不会被禁用 expect(screen.getByRole('textbox')).not.toBeDisabled(); }); }); diff --git a/src/components/__tests__/TopBar.test.tsx b/src/components/__tests__/TopBar.test.tsx index a538fc5..e3b9460 100644 --- a/src/components/__tests__/TopBar.test.tsx +++ b/src/components/__tests__/TopBar.test.tsx @@ -6,7 +6,6 @@ import TopBar from '@/components/TopBar'; import { RouterProvider } from '@/providers/RouterProvider'; import { ThemeModeProvider } from '@/providers/ThemeModeProvider'; -// matchMedia must be mocked before ThemeModeProvider is imported Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation((query: string) => ({ diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index e2b5e63..4e05ad4 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -79,7 +79,6 @@ export default defineBackground(() => { const PROTECTED = ['contextmenu', 'copy', 'paste', 'cut', 'selectstart']; - /* 1. 屏蔽 MouseEvent.prototype.preventDefault(含 mousedown 右键) */ const _origPreventDefault = MouseEvent.prototype.preventDefault; Object.defineProperty(MouseEvent.prototype, 'preventDefault', { value: function (this: MouseEvent) { @@ -93,7 +92,6 @@ export default defineBackground(() => { configurable: true, }); - /* 2. 屏蔽 Event.prototype.stopPropagation / stopImmediatePropagation */ const _origStopPropagation = Event.prototype.stopPropagation; Object.defineProperty(Event.prototype, 'stopPropagation', { value: function (this: Event) { @@ -114,7 +112,6 @@ export default defineBackground(() => { configurable: true, }); - /* 3. 拦截 document.oncontextmenu(处理 return false 方式) */ let _docOnContextMenu: unknown = null; Object.defineProperty(document, 'oncontextmenu', { get() { @@ -147,7 +144,6 @@ export default defineBackground(() => { } }); - // 4. 异步刷新请求监听(统一使用 alarms API,避免 MV3 Service Worker 被销毁导致任务丢失) onMessage(MessageAction.RELOAD_TAB, async (message) => { const { tabId, delay = 0 } = message.data; @@ -164,10 +160,8 @@ export default defineBackground(() => { const alarmName = `reload-tab-${tabId}-${Date.now()}`; - // 创建一次性 Alarm,由浏览器内核保障触发(不受 Service Worker 生命周期影响) await browser.alarms.create(alarmName, { when: Date.now() + delay }); - // 兜底清理:若 alarm 因异常未触发,delay 后 5 秒强制移除监听器并清理 alarm const cleanupTimeout = setTimeout(() => { browser.alarms.onAlarm.removeListener(alarmListener); browser.alarms.clear(alarmName).catch(() => {}); diff --git a/src/entrypoints/content/uiPopover.ts b/src/entrypoints/content/uiPopover.ts index 254df53..1a6bafb 100644 --- a/src/entrypoints/content/uiPopover.ts +++ b/src/entrypoints/content/uiPopover.ts @@ -22,7 +22,7 @@ function injectStyles(): void { font-size: 13px; line-height: 1.5; opacity: 0; - visibility: hidden; /* 💡 1. 规整隐藏状态:允许排版引擎计算尺寸,同时阻断视觉呈现 */ + visibility: hidden; transform: translateY(-8px); transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease; pointer-events: none; diff --git a/src/pages/Base64Converter/TextMode.tsx b/src/pages/Base64Converter/TextMode.tsx index 1079fc2..41f4d1b 100644 --- a/src/pages/Base64Converter/TextMode.tsx +++ b/src/pages/Base64Converter/TextMode.tsx @@ -22,12 +22,10 @@ interface TextModeProps { export default function TextMode({ onSwitchToImageMode }: TextModeProps = {}) { const { t } = useI18n('base64Converter'); - // 1. 纯净的核心源状态机:只保留输入源和转换方向 const [input, setInput] = useState(''); const [debouncedInput, setDebouncedInput] = useState(''); const [direction, setDirection] = useState<'encode' | 'decode'>('encode'); - // 2. 文本高频敲击防抖大闸:斩断频繁进行文本转 Base64 带来的 CPU 计算过热 useEffect(() => { const handle = setTimeout(() => { setDebouncedInput(input); @@ -35,7 +33,6 @@ export default function TextMode({ onSwitchToImageMode }: TextModeProps = {}) { return () => clearTimeout(handle); }, [input]); - // 3. 右键联动数据上下文:优雅原地合并受控状态 const handleContextMenuData = useCallback((payload: string) => { setInput(payload); setDebouncedInput(payload); diff --git a/src/pages/Base64Converter/index.tsx b/src/pages/Base64Converter/index.tsx index 32435f0..5dbd5a6 100644 --- a/src/pages/Base64Converter/index.tsx +++ b/src/pages/Base64Converter/index.tsx @@ -2,7 +2,7 @@ import { useI18n } from '@/utils/chromeI18n'; import { useStorageState } from '@/utils/useStorageState'; import type { Base64ConverterPageMode } from '@/types/storage'; import TextMode from './TextMode'; -import Base64ConverterSection from './Base64ConverterSection'; // ✅ 正确对接全新的一体化大组件 +import Base64ConverterSection from './Base64ConverterSection'; import SwitchButtonGroup from '@/components/SwitchButtonGroup'; const VALID_PAGE_MODES: readonly Base64ConverterPageMode[] = ['text', 'file', 'image']; diff --git a/src/pages/JsonTools/DiffResult.tsx b/src/pages/JsonTools/DiffResult.tsx index 7fb4fbd..58cc318 100644 --- a/src/pages/JsonTools/DiffResult.tsx +++ b/src/pages/JsonTools/DiffResult.tsx @@ -229,5 +229,3 @@ const stringifyMultiline = (v: unknown, depth: number): string => { return formatPrimitive(v); } }; - -// 💡 彻底移除了文件底部引发 TS2484 冲突的 export type { DiffResultProps } 声明 diff --git a/src/pages/JsonTools/JsonConvertSection.tsx b/src/pages/JsonTools/JsonConvertSection.tsx index e2458bb..e35c76b 100644 --- a/src/pages/JsonTools/JsonConvertSection.tsx +++ b/src/pages/JsonTools/JsonConvertSection.tsx @@ -75,7 +75,7 @@ export default function JsonConvertSection({ placeholder={t(`jsonFormat:${pk}InputPlaceholder`)} value={input} onChange={setInput} - externalError={error || runtimeError || undefined} // 融合语法错误与运行时转换错误 + externalError={error || runtimeError || undefined} showClear={true} allowCopy={true} minRows={7} @@ -86,14 +86,12 @@ export default function JsonConvertSection({ {/* Result display */} {result && result.output ? (
- {/* 结果栏精致头部 */}
{t(`jsonFormat:${pk}OutputLabel`)} - {/* 字节比对注入 tabular-nums font-mono,防止容量大小变动时字符横向抽搐 */}
{t('jsonFormat:originalSize')}:{' '} @@ -117,15 +115,11 @@ export default function JsonConvertSection({ />
- {/* 转换出的数据流承载区: - 💡 修复点:移除了互相冲突打架的 select-all 类名,仅保留纯净、支持自由划线选中的 select-text 样式 - */}
{result.output}
) : ( - /* Empty state */

{error ? t('jsonFormat:fixErrorHint') : t(`jsonFormat:${pk}EmptyHint`)} diff --git a/src/pages/JsonTools/index.tsx b/src/pages/JsonTools/index.tsx index e30e4d5..3b7fb2b 100644 --- a/src/pages/JsonTools/index.tsx +++ b/src/pages/JsonTools/index.tsx @@ -146,7 +146,7 @@ export default function Index() { value={leftInput} onChange={(val) => { setLeftInput(val); - setCurrentDiffIndex(0); // 💡 在同一个用户键盘事件中打包批处理,0 副作用开销 + setCurrentDiffIndex(0); }} error={leftError} minRows={9} @@ -157,7 +157,7 @@ export default function Index() { value={rightInput} onChange={(val) => { setRightInput(val); - setCurrentDiffIndex(0); // 💡 在同一个用户键盘事件中打包批处理,0 副作用开销 + setCurrentDiffIndex(0); }} error={rightError} minRows={9} diff --git a/src/pages/QrCode/__tests__/index.test.tsx b/src/pages/QrCode/__tests__/index.test.tsx index f4800d5..5c04ded 100644 --- a/src/pages/QrCode/__tests__/index.test.tsx +++ b/src/pages/QrCode/__tests__/index.test.tsx @@ -6,19 +6,16 @@ vi.mock('lucide-react', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - // 增量伪造需要高精嗅探的 QrCode 核心定位图标 QrCode: () =>

Icon
, }; }); -// Mock useSnackbar vi.mock('@/components/GlobalSnackbar', () => ({ useSnackbar: () => ({ showMessage: vi.fn(), }), })); -// Mock getEntryPointType(保留原厂其他特征配置,仅模拟入口路由环境) vi.mock('@/config/features', async (importOriginal) => { const actual = await importOriginal(); return { @@ -27,7 +24,6 @@ vi.mock('@/config/features', async (importOriginal) => { }; }); -// Mock 高频变化的子组件,收拢断言边界 vi.mock('@/components/QrCodePreview', () => ({ default: () =>
QrCodePreview
, })); @@ -36,7 +32,6 @@ vi.mock('@/components/ImageUploader', () => ({ default: () =>
ImageUploader
, })); -// Mock QRious 动态图像离屏生成引擎 vi.mock('qrious', () => ({ default: vi.fn().mockImplementation(() => ({ toDataURL: () => 'data:image/png;base64,mock', diff --git a/src/pages/StorageCleaner/StorageOptionsGrid.tsx b/src/pages/StorageCleaner/StorageOptionsGrid.tsx index 6f2dfd9..07a4334 100644 --- a/src/pages/StorageCleaner/StorageOptionsGrid.tsx +++ b/src/pages/StorageCleaner/StorageOptionsGrid.tsx @@ -10,7 +10,7 @@ interface StorageOptionsGridProps extends React.HTMLAttributes { options: StorageCleanerOptions; sizes: Record; allSelected: boolean; - someSelected: boolean; // 重新激活半选状态 + someSelected: boolean; onOptionChange: (key: keyof StorageCleanerOptions) => void; onSelectAll: (checked: boolean) => void; } @@ -36,9 +36,7 @@ export default function StorageOptionsGrid({ { key: 'serviceWorkers', isCount: true }, ]; - // 2. 处理全选栏点击事件:包裹整个栏变成超级热区 const handleToggleAll = () => { - // 如果当前已经是全选,点击则取消全选;否则,点击就是全选 onSelectAll(!allSelected); }; diff --git a/src/pages/StorageCleaner/index.tsx b/src/pages/StorageCleaner/index.tsx index 7456dfb..b582639 100644 --- a/src/pages/StorageCleaner/index.tsx +++ b/src/pages/StorageCleaner/index.tsx @@ -1,4 +1,4 @@ -import { Loader2 } from 'lucide-react'; // 引入标准的高级阻尼 Spinner 图标 +import { Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import StorageCleanerConfirm from '@/pages/StorageCleaner/StorageCleanerConfirm'; import { useStorageCleaner } from './useStorageCleaner'; diff --git a/src/pages/StorageCleaner/useStorageCleaner.ts b/src/pages/StorageCleaner/useStorageCleaner.ts index 28dc5e1..5910df4 100644 --- a/src/pages/StorageCleaner/useStorageCleaner.ts +++ b/src/pages/StorageCleaner/useStorageCleaner.ts @@ -18,7 +18,7 @@ import { } from '@/utils/storageCleaner'; import { MessageAction, sendMessage } from '@/utils/messages'; import { useI18n } from '@/utils/chromeI18n'; -import { toast } from 'sonner'; // 1. 直接引用 shadcn 推荐的 Sonner 单例通知,踢出回调依赖 +import { toast } from 'sonner'; const DEFAULT_OPTIONS: StorageCleanerOptions = { localStorage: true, diff --git a/src/pages/TextStatistics/index.tsx b/src/pages/TextStatistics/index.tsx index a78c55a..ef96961 100644 --- a/src/pages/TextStatistics/index.tsx +++ b/src/pages/TextStatistics/index.tsx @@ -15,7 +15,6 @@ export default function Index() { useContextMenuData({ featureKey: 'textStatistics', onData: handleContextMenuData }); - // 实时计算统计信息,由 useMemo 拦截非必要计算 const stats = useMemo(() => getTextStats(text), [text]); const statItems = [ diff --git a/src/pages/Timestamp/LiveClock.tsx b/src/pages/Timestamp/LiveClock.tsx index 5e5e7b9..a314758 100644 --- a/src/pages/Timestamp/LiveClock.tsx +++ b/src/pages/Timestamp/LiveClock.tsx @@ -4,7 +4,7 @@ import CopyButton from '@/components/CopyButton'; import { useSnackbar } from '@/components/GlobalSnackbar'; import type { UnitType } from './constants'; import { useI18n } from '@/utils/chromeI18n'; -import { cn } from '@/lib/utils'; // 引入标准的 shadcn 工具函数 +import { cn } from '@/lib/utils'; interface LiveClockProps extends React.HTMLAttributes { unit: UnitType; @@ -24,12 +24,10 @@ const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClock }; }); - // 始终保持外部回调指针最新 useEffect(() => { onUseNowRef.current = onUseNow; }, [onUseNow]); - // 2. 高频高灵敏度计时器 (200ms 刷新率) useEffect(() => { const tick = () => { const rightNow = Date.now(); diff --git a/src/pages/Timestamp/ResultView.tsx b/src/pages/Timestamp/ResultView.tsx index c43be42..71087a7 100644 --- a/src/pages/Timestamp/ResultView.tsx +++ b/src/pages/Timestamp/ResultView.tsx @@ -4,14 +4,14 @@ import CopyButton from '@/components/CopyButton'; import type { UnitType } from './constants'; import { DATE_FORMAT } from './constants'; import { useI18n } from '@/utils/chromeI18n'; -import { cn } from '@/lib/utils'; // shadcn 核心类名合并工具 +import { cn } from '@/lib/utils'; interface ResultViewProps extends React.HTMLAttributes { result: string; mode: 'ts2dt' | 'dt2ts'; unit: UnitType; zone: string; - /** 无结果时是否渲染占位(桌面端右栏使用),默认 false */ + showEmptyPlaceholder?: boolean; } @@ -27,7 +27,6 @@ const ResultView = React.memo( }: ResultViewProps) => { const { t } = useI18n('timestamp'); - // 严谨计算时间衍生的附加时区/相对时间状态 const extraInfo = useMemo(() => { if (!result) return null; const d = @@ -44,7 +43,6 @@ const ResultView = React.memo( }; }, [result, mode, zone, unit]); - // 1. 空状态骨架面板:优雅匹配 shadcn 的中性灰色居中占位 if (!result) { if (!showEmptyPlaceholder) return null; return ( @@ -95,7 +93,7 @@ const ResultView = React.memo( {item.value} diff --git a/src/pages/Timestamp/index.tsx b/src/pages/Timestamp/index.tsx index 0fc6ad5..e554860 100644 --- a/src/pages/Timestamp/index.tsx +++ b/src/pages/Timestamp/index.tsx @@ -76,7 +76,7 @@ export default function Index() { ]} onChange={(v) => setUnit(v as 'ms' | 's')} size="small" - className="sm:w-auto shrink-0" // 窄屏下全宽,宽屏下自适应收缩 + className="sm:w-auto shrink-0" />