diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ff35225..4fbef56 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -102,7 +102,7 @@ pages/FeatureName/ ### 代码分割 -`wxt.config.ts` 通过 `manualChunksForHtmlOnly()` 自动分组 vendor 依赖(vendor-react、vendor-i18n、vendor-qr、vendor-dnd、vendor-markdown),无需手动配置。 +`wxt.config.ts` 通过 `manualChunksForHtmlOnly()` 自动分组 vendor 依赖(vendor-react、vendor-i18n、vendor-qr、vendor-dnd),无需手动配置。 ### 代码风格 diff --git a/AGENTS.md b/AGENTS.md index 568c80b..8a9b20d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,7 +142,6 @@ src/pages/FeatureName/ - `@webext-core/messaging` — 扩展消息通信 - `@dnd-kit` — 拖拽排序(用于页面顺序管理) -- `marked` — Markdown 解析 - `qrious` + `qr-scanner` — 二维码生成与解析 - `dayjs` — 日期处理(时间戳转换) - `sonner` — Toast 通知(替代传统 snackbar) diff --git a/README.md b/README.md index 8ec821e..339b113 100644 --- a/README.md +++ b/README.md @@ -63,11 +63,6 @@ - **文件转换**: 支持文件与 Base64 字符串互转. - **图像预览**: 支持图片 Base64 编码与实时预览. -### 📝 Markdown ↔ HTML 转换 - -- **Markdown 转 HTML**: 实时预览 Markdown 渲染结果,支持导出 HTML 源码. -- **HTML 转 Markdown**: 将 HTML 内容转换为 Markdown 格式. - ## 技术栈 - **框架**: [WXT (Web Extension Toolkit)](https://wxt.dev/) diff --git a/package-lock.json b/package-lock.json index 68d015a..82265b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "clsx": "^2.1.1", "dayjs": "^1.11.20", "lucide-react": "^1.16.0", - "marked": "^18.0.4", "qr-scanner": "^1.4.2", "qrious": "^4.0.2", "react": "^19.2.6", @@ -8388,18 +8387,6 @@ "url": "https://github.com/sponsors/fregante" } }, - "node_modules/marked": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.4.tgz", - "integrity": "sha512-c/BTaKzg0G6ezQx97DAkYU7k0HM6ys0FqYeKBL6hlBByZwy+ycA1+f0vDdjMHKKeEjdgkx0GOv9Il6D+85cOqA==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, "node_modules/marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", diff --git a/package.json b/package.json index 576d168..07dd898 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "clsx": "^2.1.1", "dayjs": "^1.11.20", "lucide-react": "^1.16.0", - "marked": "^18.0.4", "qr-scanner": "^1.4.2", "qrious": "^4.0.2", "react": "^19.2.6", diff --git a/public/_locales/zh/messages.json b/public/_locales/zh/messages.json index 9063a45..48d5eb7 100644 --- a/public/_locales/zh/messages.json +++ b/public/_locales/zh/messages.json @@ -131,22 +131,6 @@ "message": "文本、文件与图像的 Base64 编码转换", "description": "Translation key: base64Converter_description" }, - "markdownToHtml_title": { - "message": "Markdown 转 HTML", - "description": "Translation key: markdownToHtml_title" - }, - "markdownToHtml_description": { - "message": "实时 Markdown 转换与 HTML 预览", - "description": "Translation key: markdownToHtml_description" - }, - "htmlToMarkdown_title": { - "message": "HTML 转 Markdown", - "description": "Translation key: htmlToMarkdown_title" - }, - "htmlToMarkdown_description": { - "message": "实时 HTML 转换与 Markdown 预览", - "description": "Translation key: htmlToMarkdown_description" - }, "rightClickRestorer_title": { "message": "右键恢复", "description": "Translation key: rightClickRestorer_title" @@ -291,54 +275,6 @@ "message": "解码大小", "description": "Translation key: base64Converter_decodedSize" }, - "htmlToMarkdown_pageTitle": { - "message": "HTML 转 Markdown", - "description": "Translation key: htmlToMarkdown_pageTitle" - }, - "htmlToMarkdown_splitMode": { - "message": "分屏", - "description": "Translation key: htmlToMarkdown_splitMode" - }, - "htmlToMarkdown_previewMode": { - "message": "预览", - "description": "Translation key: htmlToMarkdown_previewMode" - }, - "htmlToMarkdown_markdownMode": { - "message": "Markdown", - "description": "Translation key: htmlToMarkdown_markdownMode" - }, - "htmlToMarkdown_clear": { - "message": "清空", - "description": "Translation key: htmlToMarkdown_clear" - }, - "htmlToMarkdown_inputLabel": { - "message": "HTML 输入", - "description": "Translation key: htmlToMarkdown_inputLabel" - }, - "htmlToMarkdown_previewLabel": { - "message": "Markdown 预览", - "description": "Translation key: htmlToMarkdown_previewLabel" - }, - "htmlToMarkdown_markdownOutputLabel": { - "message": "Markdown 输出", - "description": "Translation key: htmlToMarkdown_markdownOutputLabel" - }, - "htmlToMarkdown_inputPlaceholder": { - "message": "在此输入 HTML 内容...", - "description": "Translation key: htmlToMarkdown_inputPlaceholder" - }, - "htmlToMarkdown_charCount": { - "message": "{{count}} 字符", - "description": "Translation key: htmlToMarkdown_charCount" - }, - "htmlToMarkdown_download": { - "message": "下载", - "description": "Translation key: htmlToMarkdown_download" - }, - "htmlToMarkdown_emptyHint": { - "message": "输入 HTML 内容以查看转换结果", - "description": "Translation key: htmlToMarkdown_emptyHint" - }, "jsonDiff_pageTitle": { "message": "JSON 差异比较", "description": "Translation key: jsonDiff_pageTitle" @@ -543,54 +479,6 @@ "message": "解析 Payload 失败:", "description": "Translation key: jwt_errors_parsePayloadFailed" }, - "markdownToHtml_pageTitle": { - "message": "Markdown 转 HTML", - "description": "Translation key: markdownToHtml_pageTitle" - }, - "markdownToHtml_splitMode": { - "message": "分屏", - "description": "Translation key: markdownToHtml_splitMode" - }, - "markdownToHtml_previewMode": { - "message": "预览", - "description": "Translation key: markdownToHtml_previewMode" - }, - "markdownToHtml_htmlMode": { - "message": "HTML", - "description": "Translation key: markdownToHtml_htmlMode" - }, - "markdownToHtml_clear": { - "message": "清空", - "description": "Translation key: markdownToHtml_clear" - }, - "markdownToHtml_print": { - "message": "打印", - "description": "Translation key: markdownToHtml_print" - }, - "markdownToHtml_download": { - "message": "下载", - "description": "Translation key: markdownToHtml_download" - }, - "markdownToHtml_inputLabel": { - "message": "Markdown 输入", - "description": "Translation key: markdownToHtml_inputLabel" - }, - "markdownToHtml_previewLabel": { - "message": "实时预览", - "description": "Translation key: markdownToHtml_previewLabel" - }, - "markdownToHtml_htmlOutputLabel": { - "message": "HTML 输出", - "description": "Translation key: markdownToHtml_htmlOutputLabel" - }, - "markdownToHtml_inputPlaceholder": { - "message": "在此输入 Markdown 内容...", - "description": "Translation key: markdownToHtml_inputPlaceholder" - }, - "markdownToHtml_charCount": { - "message": "{{count}} 字符", - "description": "Translation key: markdownToHtml_charCount" - }, "qrCode_pageTitle": { "message": "二维码工具", "description": "Translation key: qrCode_pageTitle" diff --git a/src/config/__tests__/features.test.ts b/src/config/__tests__/features.test.ts index cd273dd..ea1feda 100644 --- a/src/config/__tests__/features.test.ts +++ b/src/config/__tests__/features.test.ts @@ -9,8 +9,8 @@ import { describe('features', () => { describe('FEATURES', () => { - it('should have 11 features defined', () => { - expect(FEATURES).toHaveLength(11); + it('should have 9 features defined', () => { + expect(FEATURES).toHaveLength(9); }); it('should have all required properties for each feature', () => { @@ -95,7 +95,7 @@ describe('features', () => { describe('getAllFeatureKeys', () => { it('should return all feature keys', () => { const allKeys = getAllFeatureKeys(); - expect(allKeys).toHaveLength(11); + expect(allKeys).toHaveLength(9); expect(allKeys).toContain('dashboard'); expect(allKeys).toContain('timestamp'); expect(allKeys).toContain('storageCleaner'); @@ -104,8 +104,6 @@ describe('features', () => { expect(allKeys).toContain('jwt'); expect(allKeys).toContain('jsonDiff'); expect(allKeys).toContain('base64Converter'); - expect(allKeys).toContain('markdownToHtml'); - expect(allKeys).toContain('htmlToMarkdown'); expect(allKeys).toContain('rightClickRestorer'); }); }); @@ -123,9 +121,9 @@ describe('features', () => { expect(pageOrder).toContain('qrCode'); }); - it('should have 10 items in page order', () => { + it('should have 8 items in page order', () => { const pageOrder = getDefaultPageOrder(); - expect(pageOrder).toHaveLength(10); + expect(pageOrder).toHaveLength(8); }); }); }); diff --git a/src/config/features.tsx b/src/config/features.tsx index ed0df11..c069717 100644 --- a/src/config/features.tsx +++ b/src/config/features.tsx @@ -9,8 +9,6 @@ import { Key, GitCompareArrows, ArrowLeftRight, - Code, - File, MousePointerClick, } from 'lucide-react'; @@ -25,8 +23,6 @@ const TextStatisticsPage = lazy(() => import('@/pages/TextStatistics')); const JwtPage = lazy(() => import('@/pages/Jwt')); const JsonToolsPage = lazy(() => import('@/pages/JsonTools')); const Base64ConverterPage = lazy(() => import('@/pages/Base64Converter')); -const MarkdownToHtmlPage = lazy(() => import('@/pages/MarkdownToHtml')); -const HtmlToMarkdownPage = lazy(() => import('@/pages/HtmlToMarkdown')); const RightClickRestorerPage = lazy(() => import('@/pages/RightClickRestorer')); export interface FeatureConfig { @@ -146,32 +142,6 @@ export const FEATURES: FeatureConfig[] = [ tab: Base64ConverterPage, }, }, - { - key: 'markdownToHtml', - labelKey: 'markdownToHtml_title', - descriptionKey: 'markdownToHtml_description', - themeColorKey: 'secondary', - icon: Code, - defaultVisible: true, - components: { - popup: MarkdownToHtmlPage, - sidepanel: MarkdownToHtmlPage, - tab: MarkdownToHtmlPage, - }, - }, - { - key: 'htmlToMarkdown', - labelKey: 'htmlToMarkdown_title', - descriptionKey: 'htmlToMarkdown_description', - themeColorKey: 'secondary', - icon: File, - defaultVisible: true, - components: { - popup: HtmlToMarkdownPage, - sidepanel: HtmlToMarkdownPage, - tab: HtmlToMarkdownPage, - }, - }, { key: 'rightClickRestorer', labelKey: 'rightClickRestorer_title', diff --git a/src/pages/HtmlToMarkdown/index.tsx b/src/pages/HtmlToMarkdown/index.tsx deleted file mode 100644 index bf62a08..0000000 --- a/src/pages/HtmlToMarkdown/index.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { useCallback, useMemo, useState } from 'react'; -import { Download, Trash2 } from 'lucide-react'; -import { useI18n } from '@/utils/chromeI18n'; -import CopyButton from '@/components/CopyButton'; -import SwitchButtonGroup from '@/components/SwitchButtonGroup'; -import { Button } from '@/components/ui/button'; -import { useStorageState } from '@/utils/useStorageState'; -import type { HtmlToMarkdownPreviewMode } from '@/types/storage'; -import { downloadMarkdownFile, htmlToMarkdown, SAMPLE_HTML } from '@/utils/htmlToMarkdown'; -import { cn } from '@/lib/utils'; - -const isValidPreviewMode = (val: unknown): val is HtmlToMarkdownPreviewMode => - typeof val === 'string' && ['split', 'preview', 'markdown'].includes(val); - -export default function HtmlToMarkdownPage() { - const { t } = useI18n('htmlToMarkdown'); - const [previewMode, setPreviewMode] = useStorageState( - 'htmlToMarkdown/previewMode', - 'split' as HtmlToMarkdownPreviewMode, - isValidPreviewMode, - ); - const [html, setHtml] = useState(SAMPLE_HTML); - - const result = useMemo(() => htmlToMarkdown(html), [html]); - const error = result.hasError ? (result.error ?? null) : null; - - const handleModeChange = useCallback( - (newMode: HtmlToMarkdownPreviewMode) => { - setPreviewMode(newMode); - }, - [setPreviewMode], - ); - - const handleClear = useCallback(() => { - setHtml(''); - }, []); - - const handleDownload = useCallback(() => { - if (result.markdown) { - downloadMarkdownFile(result.markdown, 'converted.md'); - } - }, [result.markdown]); - - const showInput = previewMode !== 'preview'; - const showOutput = previewMode !== 'markdown'; - - return ( - /* 💡 统一间距尺寸: - - 彻底清除多余的 container max-w-7xl 这种网页大边距, - - 统一收拢为我们先前在 Dashboard 页、JSON 工具箱制定的 p-4 space-y-4 标准极客桌面规格。 - */ -
-
- {/* 工具栏集成区 */} -
- - -
- {/* 2. 重塑下载按钮:接入受控 Button,追加 active 物理微缩放动效 */} - - - {/* 重塑清空按钮 */} - -
-
- - {/* 错误提示: - - 💡 核心修复点:将硬编码的 bg-red-50 实色,完美超进化为系统的全自适应透明色变体 - */} - {error && ( -
- {error} -
- )} - - {/* 双翼/单栏联动面板展示区 */} -
- {/* HTML 输入端卡片面板 */} - {showInput && ( - /* 3. 智能聚焦框联动(Focus Ring Clamping): - - 外层容器追加 focus-within 变量追踪大闸。 - - 只要用户用鼠标点击了内部的 textarea,外层整块精巧的圆角大边框会一帧内亮起 primary 系统的深色呼吸发光环, - - 这种“全外包裹层框聚焦”的体验极大模仿了本地原生 IDE 的硬核专业体验! - */ -
- {/* 卡片头部:改用标准的灰色 bg-muted/50 */} -
- - {t('inputLabel')} - - - {t('charCount', { count: html.length })} - -
-