Skip to content

Commit

Permalink
feat: add google plug
Browse files Browse the repository at this point in the history
  • Loading branch information
cubxxw committed Dec 15, 2024
1 parent 2c664f4 commit c4d7cb6
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 26 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production",
"build": "webpack --mode production && cp public/manifest.json public/popup.html dist/",
"dev": "webpack --mode development"
},
"keywords": [],
Expand Down
12 changes: 11 additions & 1 deletion public/popup.html
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>微信读书AI助手</title>
</head>
<body>
<div id="root"></div>
<script src="popup.js"></script>
</body>
</html>
41 changes: 41 additions & 0 deletions src/background/background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import APIManager from '../utils/api';
import StorageManager from '../utils/storage';

// 监听来自内容脚本的消息
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
if (message.type === 'SUMMARIZE') {
try {
const prompt = `
请为以下内容生成一个简洁、清晰且全面的摘要,涵盖主要观点和关键信息:
内容:
${message.content}
摘要:
`;
const apiResponse = await APIManager.callOpenAI(prompt);

if (apiResponse.choices && apiResponse.choices.length > 0) {
const summary = apiResponse.choices[0].message.content.trim();
sendResponse({ summary });
} else {
throw new Error('API未返回有效的摘要。');
}
} catch (error) {
console.error('生成摘要时出错:', error);
sendResponse({ error: '生成摘要时出错,请确保API配置正确并稍后再试。' });
}

return true;
} else if (message.type === 'EXTRACT_HIGHLIGHTS') {
try {
const highlights = await StorageManager.getHighlights();
sendResponse({ highlights });
} catch (error) {
console.error('提取重点时出错:', error);
sendResponse({ error: '提取重点时出错,请稍后再试。' });
}

return true;
}
});
76 changes: 67 additions & 9 deletions src/content-scripts/contentScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ class ContentExtractor {
}

static injectToolbar() {
const toolbarContainer = document.querySelector('.readerTopBar');
if (!toolbarContainer) return;

const toolbar = document.createElement('div');
toolbar.className = 'weread-ai-toolbar';
toolbar.className = 'weread-ai-tools';
toolbar.innerHTML = `
<button id="summarize">生成摘要</button>
<button id="extract">提取重点</button>
<div id="summarize" class="tool-item" title="AI生成本章摘要">
<i class="icon-summary"></i>
<span>生成摘要</span>
</div>
<div id="extract" class="tool-item" title="提取重点内容">
<i class="icon-extract"></i>
<span>提取重点</span>
</div>
`;
document.body.appendChild(toolbar);

toolbarContainer.appendChild(toolbar);
this.bindEvents();
}

Expand All @@ -35,11 +45,59 @@ class ContentExtractor {
}

private static async handleSummarize() {
const content = ContentExtractor.getCurrentChapterContent();
// 发送消息���background script处理
chrome.runtime.sendMessage({
type: 'SUMMARIZE',
content
const button = document.getElementById('summarize');
if (!(button instanceof HTMLButtonElement)) return;

try {
button.disabled = true;
button.textContent = '生成中...';

const content = ContentExtractor.getCurrentChapterContent();
const prompt = `
请对以下内容进行摘要,要求:
1. 提炼核心观点和关键信息
2. 保持逻辑清晰,层次分明
3. 语言简洁准确
4. 突出重要概念和结论
内容:${content}
`;

chrome.runtime.sendMessage({
type: 'SUMMARIZE',
content: prompt
}, (response) => {
if (response.summary) {
ContentExtractor.showSummaryModal(response.summary);
} else {
throw new Error(response.error || '生成失败');
}
});
} catch (error) {
console.error('摘要生成失败:', error);
alert('摘要生成失败,请稍后重试');
} finally {
button.disabled = false;
button.textContent = '生成摘要';
}
}

static showSummaryModal(summary: string) {
// 创建模态窗口元素
const modal = document.createElement('div');
modal.className = 'summary-modal';
modal.innerHTML = `
<div class="modal-content">
<span class="close-button">&times;</span>
<h2>章节摘要</h2>
<p>${summary}</p>
</div>
`;
document.body.appendChild(modal);

// 绑定关闭事件
modal.querySelector('.close-button')?.addEventListener('click', () => {
modal.remove();
});
}

Expand Down
53 changes: 46 additions & 7 deletions src/popup.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,53 @@
import React, { useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import APIManager from './utils/api';
import StorageManager from './utils/storage';
import './styles/global.css';

const Popup: React.FC = () => {
const [apiKey, setApiKey] = useState('');
const [apiBase, setApiBase] = useState('');
const [summary, setSummary] = useState('');
const [status, setStatus] = useState<{ message: string; type: 'success' | 'error' } | null>(null);
const [isSaving, setIsSaving] = useState(false);

useEffect(() => {
loadConfig();
}, []);

const loadConfig = async () => {
const config = await StorageManager.getConfig();
if (config) {
setApiKey(config.apiKey);
setApiBase(config.apiBase);
try {
const config = await StorageManager.getConfig();
if (config) {
setApiKey(config.apiKey);
setApiBase(config.apiBase);
}
} catch (error) {
console.error('加载配置时出错:', error);
setStatus({ message: '加载配置时出错,请稍后再试。', type: 'error' });
}
};

const handleSaveConfig = async () => {
await StorageManager.saveConfig({ apiKey, apiBase });
APIManager.setConfig({ apiKey, apiBase });
setIsSaving(true);
setStatus(null);
try {
if (!apiKey.trim()) {
throw new Error('API Key不能为空。');
}
if (!apiBase.trim()) {
throw new Error('API Base URL不能为空。');
}

await StorageManager.saveConfig({ apiKey, apiBase });
APIManager.setConfig({ apiKey, apiBase });
setStatus({ message: '配置已成功保存!', type: 'success' });
} catch (error: any) {
console.error('保存配置时出错:', error);
setStatus({ message: error.message || '保存配置时出错,请稍后再试。', type: 'error' });
} finally {
setIsSaving(false);
}
};

return (
Expand All @@ -40,7 +66,14 @@ const Popup: React.FC = () => {
value={apiBase}
onChange={e => setApiBase(e.target.value)}
/>
<button onClick={handleSaveConfig}>保存配置</button>
<button onClick={handleSaveConfig} disabled={isSaving}>
{isSaving ? '保存中...' : '保存配置'}
</button>
{status && (
<div className={`status-message ${status.type}`}>
{status.message}
</div>
)}
</div>
<div className="summary-section">
{summary && <div className="summary">{summary}</div>}
Expand All @@ -49,4 +82,10 @@ const Popup: React.FC = () => {
);
};

const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(<Popup />);
}

export default Popup;
126 changes: 121 additions & 5 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,139 @@
}

.popup {
width: 300px;
padding: 16px;
width: 350px;
padding: 20px;
font-family: Arial, sans-serif;
}

.popup h2 {
margin-bottom: 20px;
color: #1890ff;
}

.config-section {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 16px;
gap: 15px;
margin-bottom: 20px;
}

.config-section input {
padding: 8px;
padding: 10px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 14px;
}

.config-section button {
padding: 10px;
border: none;
border-radius: 4px;
background: #52c41a;
color: white;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}

.config-section button:hover {
background: #73d13d;
}

.config-section button:disabled {
background: #b7eb8f;
cursor: not-allowed;
}

.status-message {
margin-top: 10px;
padding: 8px;
border-radius: 4px;
font-size: 14px;
}

.status-message.success {
background-color: #f6ffed;
color: #389e0d;
border: 1px solid #b7eb8f;
}

.status-message.error {
background-color: #fff1f0;
color: #cf1322;
border: 1px solid #ffa39e;
}

.summary-section {
margin-top: 16px;
}

.summary {
background-color: #fafafa;
padding: 10px;
border-radius: 4px;
border: 1px solid #d9d9d9;
}

.summary-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
}

.summary-modal .modal-content {
background-color: #fff;
padding: 20px;
border-radius: 8px;
width: 80%;
max-width: 600px;
position: relative;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

.summary-modal .close-button {
position: absolute;
top: 10px;
right: 15px;
font-size: 24px;
cursor: pointer;
}

.summary-modal h2 {
margin-top: 0;
color: #1890ff;
}

.summary-modal p {
white-space: pre-wrap;
line-height: 1.6;
}

.weread-ai-tools {
display: inline-flex;
align-items: center;
margin-left: 20px;
}

.tool-item {
display: flex;
align-items: center;
padding: 6px 12px;
cursor: pointer;
color: #666;
transition: all 0.3s;
margin: 0 5px;
}

.tool-item:hover {
color: #1890ff;
background: rgba(24,144,255,0.1);
border-radius: 4px;
}
Loading

0 comments on commit c4d7cb6

Please sign in to comment.