diff --git a/src/pages/JsonTools/JsonConvertSection.tsx b/src/pages/JsonTools/JsonConvertSection.tsx
index e35c76b..05360c5 100644
--- a/src/pages/JsonTools/JsonConvertSection.tsx
+++ b/src/pages/JsonTools/JsonConvertSection.tsx
@@ -1,6 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react';
import { useI18n } from '@/utils/chromeI18n';
-import { formatByteSize } from '@/utils/textStatistics';
+import { formatBytes } from '@/utils/format';
import CopyButton from '@/components/CopyButton';
import TextInputArea from '@/components/TextInputArea';
import { validateJson } from '@/utils/jsonFormatter';
@@ -96,7 +96,7 @@ export default function JsonConvertSection({
{t('jsonFormat:originalSize')}:{' '}
- {formatByteSize(result.originalBytes)}
+ {formatBytes(result.originalBytes)}
|
diff --git a/src/pages/JsonTools/JsonFormatSection.tsx b/src/pages/JsonTools/JsonFormatSection.tsx
index 6c2ea44..ee7d169 100644
--- a/src/pages/JsonTools/JsonFormatSection.tsx
+++ b/src/pages/JsonTools/JsonFormatSection.tsx
@@ -6,7 +6,7 @@ import {
type JsonFormatResult,
validateJson,
} from '@/utils/jsonFormatter';
-import { formatByteSize } from '@/utils/textStatistics';
+import { formatBytes } from '@/utils/format';
import CopyButton from '@/components/CopyButton';
import SwitchButtonGroup from '@/components/SwitchButtonGroup';
import TextInputArea from '@/components/TextInputArea';
@@ -127,7 +127,7 @@ export default function JsonFormatSection() {
{t('jsonFormat:originalSize')}:{' '}
- {formatByteSize(result.originalBytes)}
+ {formatBytes(result.originalBytes)}
|
diff --git a/src/pages/StorageCleaner/OptionItem.tsx b/src/pages/StorageCleaner/OptionItem.tsx
index 88ba89f..f88ad8e 100644
--- a/src/pages/StorageCleaner/OptionItem.tsx
+++ b/src/pages/StorageCleaner/OptionItem.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { formatSize } from '@/utils/storageCleaner';
+import { formatBytes } from '@/utils/format';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
// 引入官方的 Checkbox 原子组件
@@ -53,7 +53,7 @@ export default function OptionItem({
checked ? 'text-primary/70' : 'text-muted-foreground/70',
)}
>
- {isCount ? `${size} ${t('storageCleaner:countUnit')}` : formatSize(size)}
+ {isCount ? `${size} ${t('storageCleaner:countUnit')}` : formatBytes(size)}
) : (
diff --git a/src/pages/TextStatistics/index.tsx b/src/pages/TextStatistics/index.tsx
index 438251e..3661285 100644
--- a/src/pages/TextStatistics/index.tsx
+++ b/src/pages/TextStatistics/index.tsx
@@ -1,5 +1,5 @@
import TextInputArea from '@/components/TextInputArea';
-import { formatByteSize } from '@/utils/textStatistics';
+import { formatBytes } from '@/utils/format';
import { useI18n } from '@/utils/chromeI18n';
import { useTextStatistics } from './useTextStatistics';
import { cn } from '@/lib/utils';
@@ -12,7 +12,7 @@ export default function Index() {
{ label: t('textStatistics:characters'), value: stats.characters },
{ label: t('textStatistics:words'), value: stats.words },
{ label: t('textStatistics:lines'), value: stats.lines },
- { label: t('textStatistics:bytes'), value: formatByteSize(stats.bytes) },
+ { label: t('textStatistics:bytes'), value: formatBytes(stats.bytes) },
];
return (
diff --git a/src/utils/__tests__/storageCleaner.test.ts b/src/utils/__tests__/storageCleaner.test.ts
index 4b2d6c2..9137a38 100644
--- a/src/utils/__tests__/storageCleaner.test.ts
+++ b/src/utils/__tests__/storageCleaner.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
-import { clearCookies, formatSize, isRestrictedUrl } from '@/utils/storageCleaner';
+import { clearCookies, isRestrictedUrl } from '@/utils/storageCleaner';
+import { formatBytes } from '@/utils/format';
describe('storageCleaner utils', () => {
describe('isRestrictedUrl', () => {
@@ -48,36 +49,36 @@ describe('storageCleaner utils', () => {
});
});
- describe('formatSize', () => {
+ describe('formatBytes', () => {
it('should return "0 B" for 0 bytes', () => {
- expect(formatSize(0)).toBe('0 B');
+ expect(formatBytes(0)).toBe('0 B');
});
it('should format bytes correctly', () => {
- expect(formatSize(500)).toBe('500 B');
+ expect(formatBytes(500)).toBe('500 B');
});
it('should format kilobytes correctly', () => {
- expect(formatSize(1024)).toBe('1.0 KB');
- expect(formatSize(1536)).toBe('1.5 KB');
- expect(formatSize(2048)).toBe('2.0 KB');
+ expect(formatBytes(1024)).toBe('1.0 KB');
+ expect(formatBytes(1536)).toBe('1.5 KB');
+ expect(formatBytes(2048)).toBe('2.0 KB');
});
it('should format megabytes correctly', () => {
- expect(formatSize(1048576)).toBe('1.00 MB');
- expect(formatSize(1572864)).toBe('1.50 MB');
- expect(formatSize(5242880)).toBe('5.00 MB');
+ expect(formatBytes(1048576)).toBe('1.00 MB');
+ expect(formatBytes(1572864)).toBe('1.50 MB');
+ expect(formatBytes(5242880)).toBe('5.00 MB');
});
it('should format gigabytes correctly', () => {
- expect(formatSize(1073741824)).toBe('1.00 GB');
- expect(formatSize(2147483648)).toBe('2.00 GB');
+ expect(formatBytes(1073741824)).toBe('1.00 GB');
+ expect(formatBytes(2147483648)).toBe('2.00 GB');
});
it('should handle edge cases', () => {
- expect(formatSize(1)).toBe('1 B');
- expect(formatSize(1023)).toBe('1023 B');
- expect(formatSize(1025)).toBe('1.0 KB');
+ expect(formatBytes(1)).toBe('1 B');
+ expect(formatBytes(1023)).toBe('1023 B');
+ expect(formatBytes(1025)).toBe('1.0 KB');
});
});
diff --git a/src/utils/__tests__/textStatistics.test.ts b/src/utils/__tests__/textStatistics.test.ts
index 308f348..81d4103 100644
--- a/src/utils/__tests__/textStatistics.test.ts
+++ b/src/utils/__tests__/textStatistics.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
-import { formatByteSize, getTextStats } from '@/utils/textStatistics';
+import { formatBytes } from '@/utils/format';
+import { getTextStats } from '@/utils/textStatistics';
describe('textStatistics utils', () => {
describe('getTextStats', () => {
@@ -49,12 +50,12 @@ describe('textStatistics utils', () => {
});
});
- describe('formatByteSize', () => {
+ describe('formatBytes', () => {
it('should format bytes correctly', () => {
- expect(formatByteSize(100)).toBe('100 B');
- expect(formatByteSize(0)).toBe('0 B');
- expect(formatByteSize(1024)).toBe('1.0 KB');
- expect(formatByteSize(1024 * 1024)).toBe('1.00 MB');
+ expect(formatBytes(100)).toBe('100 B');
+ expect(formatBytes(0)).toBe('0 B');
+ expect(formatBytes(1024)).toBe('1.0 KB');
+ expect(formatBytes(1024 * 1024)).toBe('1.00 MB');
});
});
});
diff --git a/src/utils/storageCleaner.ts b/src/utils/storageCleaner.ts
index 53aef67..f40fea2 100644
--- a/src/utils/storageCleaner.ts
+++ b/src/utils/storageCleaner.ts
@@ -1,10 +1,4 @@
import type { CleaningResult, StorageCleanerOptions, StorageCleanResult } from '@/types/storage';
-import { formatBytes } from './format';
-
-/** 格式化字节大小显示(兼容旧接口,内部委托给 formatBytes) */
-export function formatSize(bytes: number): string {
- return formatBytes(bytes);
-}
const RESTRICTED_PROTOCOLS = [
'chrome:',
diff --git a/src/utils/textStatistics.ts b/src/utils/textStatistics.ts
index 4cb6f17..ece617b 100644
--- a/src/utils/textStatistics.ts
+++ b/src/utils/textStatistics.ts
@@ -1,5 +1,3 @@
-import { formatBytes } from './format';
-
/**
* 文本统计信息接口
*/
@@ -35,7 +33,7 @@ export function getTextStats(text: string): TextStats {
const segmenter = new Intl.Segmenter(undefined, { granularity: 'word' });
const segments = segmenter.segment(text);
for (const segment of segments) {
- // isWordLike 为 true 表示该片段是“类词”的(非空格、非标点)
+ // isWordLike 为 true 表示该片段是"类词"的(非空格、非标点)
if (segment.isWordLike) {
words++;
}
@@ -44,7 +42,7 @@ export function getTextStats(text: string): TextStats {
// 降级方案:如果不支持 Intl.Segmenter,使用正则匹配英文单词
// 但对中文支持较差
const englishWords = text.match(/\b\w+\b/g) || [];
- const chineseChars = text.match(/[\u4e00-\u9fa5]/g) || [];
+ const chineseChars = text.match(/[一-龥]/g) || [];
words = englishWords.length + chineseChars.length;
}
@@ -57,13 +55,3 @@ export function getTextStats(text: string): TextStats {
return { characters, words, lines, bytes };
}
-
-/**
- * 格式化字节大小显示(兼容旧接口,内部委托给 formatBytes)
- *
- * @param bytes 字节数
- * @returns 格式化后的字符串
- */
-export function formatByteSize(bytes: number): string {
- return formatBytes(bytes);
-}
diff --git a/src/utils/useContextMenuData.ts b/src/utils/useContextMenuData.ts
index 71d353e..8c2b1c1 100644
--- a/src/utils/useContextMenuData.ts
+++ b/src/utils/useContextMenuData.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect } from 'react';
+import { useEffect } from 'react';
import { storageUtil } from '@/utils/chromeStorage';
import type { ContextMenuPendingData, PageType } from '@/types/storage';
@@ -23,44 +23,58 @@ export interface UseContextMenuDataOptions {
* 3. Hook 会自动从 storage 中读取并消费匹配的数据
*/
export function useContextMenuData({ featureKey, onData }: UseContextMenuDataOptions): void {
- const checkAndConsumeData = useCallback(async () => {
- try {
- const data = await storageUtil.get(STORAGE_KEY, undefined);
-
- if (!data) return;
-
- if (data.featureKey !== featureKey) return;
-
- if (Date.now() - data.timestamp > CONTEXT_MENU_DATA_EXPIRY_MS) {
- await storageUtil.remove(STORAGE_KEY);
- return;
- }
-
- await storageUtil.remove(STORAGE_KEY);
-
- onData(data.payload);
- } catch (error) {
- console.error('[useContextMenuData] 处理右键菜单数据失败:', error);
- }
- }, [featureKey, onData]);
-
useEffect(() => {
- checkAndConsumeData();
- }, [checkAndConsumeData]);
+ const checkAndConsumeData = async () => {
+ try {
+ const data = await storageUtil.get(STORAGE_KEY, undefined);
+
+ if (!data) return;
+
+ if (data.featureKey !== featureKey) return;
+
+ if (Date.now() - data.timestamp > CONTEXT_MENU_DATA_EXPIRY_MS) {
+ await storageUtil.remove(STORAGE_KEY);
+ return;
+ }
+
+ await storageUtil.remove(STORAGE_KEY);
+
+ onData(data.payload);
+ } catch (error) {
+ console.error('[useContextMenuData] 处理右键菜单数据失败:', error);
+ }
+ };
+
+ void checkAndConsumeData();
+ }, [featureKey, onData]);
useEffect(() => {
const handleStorageChange = (changes: { [key: string]: chrome.storage.StorageChange }) => {
if (changes[STORAGE_KEY]) {
const newData = changes[STORAGE_KEY].newValue as ContextMenuPendingData | null;
if (newData && newData.featureKey === featureKey) {
- checkAndConsumeData();
+ void (async () => {
+ try {
+ const data = await storageUtil.get(STORAGE_KEY, undefined);
+ if (!data) return;
+ if (data.featureKey !== featureKey) return;
+ if (Date.now() - data.timestamp > CONTEXT_MENU_DATA_EXPIRY_MS) {
+ await storageUtil.remove(STORAGE_KEY);
+ return;
+ }
+ await storageUtil.remove(STORAGE_KEY);
+ onData(data.payload);
+ } catch (error) {
+ console.error('[useContextMenuData] 处理右键菜单数据失败:', error);
+ }
+ })();
}
}
};
chrome.storage.onChanged.addListener(handleStorageChange);
return () => chrome.storage.onChanged.removeListener(handleStorageChange);
- }, [featureKey, checkAndConsumeData]);
+ }, [featureKey, onData]);
}
/**