import { useState } from 'react'; import { ExtractButton } from './components/ExtractButton'; import { OptionsPanel } from './components/OptionsPanel'; import { PreviewModal } from './components/PreviewModal'; import { useChromeStorage } from '../hooks/useChromeStorage'; import { copyToClipboard } from '../utils/download'; import type { ExtractedContent } from '../types'; // 状态消息类型 type StatusType = 'idle' | 'loading' | 'success' | 'error'; export function Popup() { const { settings, saveSettings, loading: settingsLoading } = useChromeStorage(); const [extractedContent, setExtractedContent] = useState(null); const [status, setStatus] = useState<{ type: StatusType; message: string }>({ type: 'idle', message: '' }); const [showPreview, setShowPreview] = useState(false); const [mode, setMode] = useState<'auto' | 'selection'>('auto'); // 显示状态消息 const showStatus = (type: StatusType, message: string) => { setStatus({ type, message }); if (type !== 'loading') { setTimeout(() => setStatus({ type: 'idle', message: '' }), 3000); } }; // 获取当前标签页 const getCurrentTab = async (): Promise => { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) throw new Error('无法获取当前标签页'); return tab; }; // 提取内容 const handleExtract = async () => { try { showStatus('loading', mode === 'selection' ? '请在页面上选择内容...' : '正在提取内容...'); const tab = await getCurrentTab(); if (!tab.id) throw new Error('标签页无效'); // 发送消息给content script const response = await chrome.tabs.sendMessage(tab.id, { action: mode === 'selection' ? 'extractSelection' : 'extract' }); if (response.success && response.data) { setExtractedContent(response.data); setShowPreview(true); showStatus('success', '提取成功!'); } else { showStatus('error', response.error || '提取失败'); } } catch (error) { console.error('提取失败:', error); showStatus('error', error instanceof Error ? error.message : '提取失败'); } }; // 复制到剪贴板 const handleCopy = async () => { if (!extractedContent) return; try { const markdown = generateMarkdown(extractedContent, { includeTitle: settings.includeTitle, includeUrl: settings.includeUrl }); await copyToClipboard(markdown); showStatus('success', '已复制到剪贴板!'); setTimeout(() => { window.close(); }, 1000); } catch { showStatus('error', '复制失败'); } }; // 下载文件 - 通过 background script 直接下载到默认目录 const handleDownload = async () => { if (!extractedContent) return; try { const markdown = generateMarkdown(extractedContent, { includeTitle: settings.includeTitle, includeUrl: settings.includeUrl }); const filename = `${extractedContent.title.replace(/[<>:"/\\|?*]/g, '_').substring(0, 100)}.md`; // 发送消息给 background script 处理下载 await chrome.runtime.sendMessage({ action: 'download', filename: filename, content: markdown }); showStatus('success', '开始下载...'); setTimeout(() => { window.close(); }, 1000); } catch { showStatus('error', '下载失败'); } }; // 生成Markdown const generateMarkdown = (content: ExtractedContent, options: { includeTitle: boolean; includeUrl: boolean }): string => { let md = ''; // Frontmatter md += '---\n'; if (options.includeTitle) { md += `title: "${content.title.replace(/"/g, '\\"')}"\n`; } if (options.includeUrl) { md += `source: ${content.url}\n`; } md += '---\n\n'; // 标题 if (options.includeTitle) { md += `# ${content.title}\n\n`; } // 原文链接 if (options.includeUrl) { md += `> 原文链接: [${content.title}](${content.url})\n\n`; } // 内容 md += content.markdown; return md; }; if (settingsLoading) { return (
); } return (
{/* 头部 */}

ReadMD

网页转 Markdown

{/* 主要内容区 */}
{/* 状态消息 */} {status.message && (
{status.message}
)} {/* 提取按钮 */} {/* 选项面板 */}
{/* 页脚 */}
{/* 预览弹窗 */} {showPreview && extractedContent && ( setShowPreview(false)} onCopy={handleCopy} onDownload={handleDownload} /> )}
); }