commit 760f3ec0d0fc16aef683d5341fad24b9a6c8ff0e Author: ytc1012 <18001193130@163.com> Date: Wed Dec 10 18:18:30 2025 +0800 first commit diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..1e2b006 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(wc:*)" + ] + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e928755 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,227 @@ +# BlurText 更新日志 + +## v2.0.0 (2025-01-10) - 文本选择模式 + +### 🎉 新功能 + +#### **双模式支持** +现在 BlurText 支持两种模糊模式,满足不同场景需求: + +1. **🖱️ 元素模式**(原有功能) + - 鼠标悬停查看高亮 + - 点击元素进行模糊 + - 适用于结构化内容(表格、卡片等) + +2. **📝 文本选择模式**(新增功能)⭐ + - 拖动鼠标选择任意文本 + - 点击浮动按钮模糊选中内容 + - **完美解决纯文本节点的模糊问题** + - 适用于文章、段落等纯文本内容 + +--- + +### 🔧 技术实现 + +#### **文本选择模式核心特性** + +使用标准 Selection API 实现: +```javascript +// 1. 监听文本选择 +document.addEventListener('mouseup', handleTextSelection); + +// 2. 获取选中文本 +const selection = window.getSelection(); +const range = selection.getRangeAt(0); + +// 3. 包裹并模糊 +const span = document.createElement('span'); +range.surroundContents(span); +span.classList.add('blurtext-blurred'); +``` + +#### **智能浮动按钮** +- 自动定位在选中文本下方 +- 渐入动画效果 +- 点击后自动隐藏 + +#### **错误处理** +- 捕获复杂 HTML 结构的异常 +- 提供友好的错误提示 +- 不会破坏页面功能 + +--- + +### 🎨 UI 改进 + +#### **Popup 界面升级** +- 新增模式切换按钮(图形化设计) +- 动态使用提示(根据模式显示不同说明) +- 更清晰的视觉反馈 + +#### **模式图标** +- 🖱️ 元素模式 - 代表点击操作 +- 📝 文本选择模式 - 代表文本选择 + +#### **CSS 样式增强** +- 新增模糊按钮样式(渐变背景 + 阴影) +- 按钮动画效果(弹出 + 悬停缩放) +- 更好的视觉层级 + +--- + +### 📝 使用指南 + +#### **元素模式使用方法** +``` +1. 点击扩展图标打开 Popup +2. 选择"元素模式" +3. 点击"开启模糊模式" +4. 鼠标悬停查看高亮 +5. 点击元素进行模糊 +``` + +#### **文本选择模式使用方法** ⭐ +``` +1. 点击扩展图标打开 Popup +2. 选择"文本选择模式" +3. 点击"开启模糊模式" +4. 拖动鼠标选择文本 +5. 点击浮动按钮"🔒 模糊选中文本" +6. 可以多次选择和模糊 +``` + +--- + +### 🆚 两种模式对比 + +| 特性 | 元素模式 | 文本选择模式 | +|------|---------|-------------| +| **操作方式** | 点击元素 | 拖动选择文本 | +| **适用场景** | 结构化内容 | 纯文本内容 | +| **精确度** | 按元素边界 | 按字符级别 | +| **DOM 修改** | 不修改 | 添加 span 包裹 | +| **预览方式** | 悬停高亮 | 浏览器原生选择 | +| **典型使用** | 表格、卡片、按钮 | 段落、文章、评论 | + +--- + +### 🎯 使用场景示例 + +#### **场景 1:文章中的敏感信息** +```html +
用户张三的手机号是138-8888-9999,邮箱是zhang@example.com
+``` + +**元素模式**: +- 点击 → 模糊整个段落 ❌ + +**文本选择模式**: +- 选择"138-8888-9999" → 只模糊手机号 ✅ +- 选择"zhang@example.com" → 只模糊邮箱 ✅ + +#### **场景 2:表格数据** +```html +| 姓名 | 李四 |
| 工号 | E12345 |
| 姓名 | +张三 | +
+ 手机号: + 138-8888-9999 +
+``` +- 鼠标悬停 `` → 高亮手机号 +- 点击 → 模糊手机号 ✅ + +### 场景 3:纯文本(需要手动处理) +```html +用户 张三 的手机号是 138-8888-9999
+``` +- 悬停任意位置 → 高亮整个 `` +- 点击 → 模糊整段文字 +- **如需只模糊"张三"**:先手动编辑 HTML,用 `张三` 包裹 + +--- + +## 🔧 技术实现 + +### 简化后的核心逻辑 + +**content.js 核心函数**: + +```javascript +// 1. 处理点击 - 简单直接 +function handleClick(e) { + if (!isBlurMode) return; + e.preventDefault(); + e.stopPropagation(); + + let target = e.target; + if (target.classList.contains('blurtext-hint')) return; + + // 直接切换目标元素的模糊状态 + toggleBlur(target); +} + +// 2. 处理悬停 - 高亮预览 +function handleMouseOver(e) { + if (!isBlurMode) return; + + const target = e.target; + if (target.classList.contains('blurtext-hint') || + target.classList.contains('blurtext-blurred')) { + return; + } + + removeAllHighlights(); + target.classList.add('blurtext-highlight'); +} + +// 3. 移除高亮 - 简单清理 +function removeAllHighlights() { + const highlighted = document.querySelectorAll('.blurtext-highlight'); + highlighted.forEach(el => { + el.classList.remove('blurtext-highlight'); + }); +} +``` + +**代码行数对比**: +- 之前:531 行(包含文本节点检测、智能识别、预览管理) +- 现在:约 250 行(移除了约 280 行复杂逻辑) +- 减少 53% 代码量,提升稳定性 + +--- + +## 🧪 测试步骤 + +1. 刷新扩展(`chrome://extensions/`) +2. 打开任意网页 +3. 点击扩展图标,开启模糊模式 +4. **测试基础模糊**: + - 鼠标悬停在任意元素上 → 看到蓝色边框 + - 点击元素 → 成功模糊 ✅ + - 再次点击 → 取消模糊 ✅ +5. **测试强度调整**: + - 打开 popup,拖动滑块 + - 已模糊的元素实时更新强度 ✅ +6. **测试批量清除**: + - 点击"清除所有模糊"按钮 + - 所有模糊效果移除 ✅ + +--- + +## 📝 后续计划 + +基于简单稳定的架构,未来可以考虑: + +1. **框选模糊**:拖拽鼠标框选区域进行模糊 +2. **模糊记忆**:页面刷新后保持模糊状态 +3. **快捷键**:Ctrl+B 快速切换模糊模式 +4. **预设模板**:保存常用的模糊配置 +5. **图片模糊**:支持模糊图片元素 + +--- + +## 💡 开发经验总结 + +**设计原则**: +1. **简单优于复杂**:不要过度设计,用户需要的是稳定可靠的功能 +2. **所见即所得**:悬停预览必须和点击结果完全一致 +3. **DOM 操作要谨慎**:频繁的 DOM 修改容易引入 bug +4. **渐进增强**:先做好基础功能,再逐步添加高级特性 + +**避免的陷阱**: +- ❌ 自动检测和包裹文本节点(DOM 操作复杂,容易出问题) +- ❌ 临时元素管理(生命周期难以控制) +- ❌ 智能识别(正则匹配不够可靠) +- ✅ 让用户明确点击目标元素(简单直接) diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..663a70d --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,186 @@ +# 安装和调试指南 + +## 🚀 快速开始 + +### 第一步:生成图标 + +1. 在浏览器中打开 `icon-generator.html` 文件 +2. 右键点击每个图标,选择"另存为图像" +3. 将图标保存到 `icons/` 文件夹,文件名分别为: + - `icon16.png` + - `icon32.png` + - `icon48.png` + - `icon128.png` + +> 💡 **提示**: 如果右键保存不方便,可以点击每个图标下方的"下载"按钮 + +### 第二步:安装扩展 + +1. 打开 Chrome 浏览器 +2. 在地址栏输入 `chrome://extensions/` 并回车 +3. 开启右上角的"**开发者模式**"开关 +4. 点击"**加载已解压的扩展程序**" +5. 选择 `blurweb` 文件夹 +6. 看到扩展出现在列表中,说明安装成功! + +### 第三步:测试功能 + +#### 方法一:使用测试页面 + +1. 在浏览器中打开 `test.html` 文件 +2. 按照页面上的说明进行测试 + +#### 方法二:测试任意网页 + +1. 打开任意网页(如百度、GitHub 等) +2. 点击浏览器工具栏的 BlurText 图标(紫色图标) +3. 点击"开启模糊模式"按钮 +4. 在网页上点击任意文字,观察模糊效果 + +## 🔍 故障排查 + +### 问题 1:点击文字没有反应 + +**可能原因和解决方法:** + +#### 1. 扩展未正确加载 +- 检查:访问 `chrome://extensions/`,确认 BlurText 已启用 +- 解决:如果未启用,点击开关启用;如果有错误提示,重新加载扩展 + +#### 2. Content Script 未注入 +- 检查:按 F12 打开开发者工具,查看 Console 标签页 +- 应该看到:`[BlurText] Content script loaded` +- 如果没有,刷新页面(F5)后重试 + +#### 3. 消息未发送成功 +- 检查:右键点击扩展图标 → 检查弹出窗口 → 查看 Console +- 应该看到:`[BlurText] Toggle button clicked` 和 `[BlurText] Message sent successfully` +- 如果看到错误,尝试重新加载扩展 + +#### 4. 页面刷新问题 +- 解决:在安装扩展后,刷新所有打开的网页 +- 或者重新打开新标签页测试 + +### 问题 2:提示"请刷新页面后重试" + +**原因:** Content Script 尚未注入到页面 + +**解决:** +1. 刷新当前页面(F5) +2. 再次点击扩展图标 +3. 点击"开启模糊模式" + +### 问题 3:扩展图标不显示 + +**原因:** 图标文件未正确生成或路径错误 + +**解决:** +1. 确认 `icons/` 文件夹存在 +2. 确认包含所有 4 个图标文件 +3. 文件名必须完全匹配:`icon16.png`, `icon32.png`, `icon48.png`, `icon128.png` +4. 重新加载扩展 + +### 问题 4:某些网页无法使用 + +**原因:** 浏览器限制扩展在特殊页面运行 + +**说明:** 以下页面不支持扩展: +- `chrome://` 开头的页面(如设置页、扩展页) +- `chrome-extension://` 开头的页面 +- Chrome Web Store 页面 +- 新标签页(某些情况下) + +**解决:** 在普通网页上使用(如百度、GitHub、新闻网站等) + +## 🐛 调试技巧 + +### 查看 Content Script 日志 + +1. 在网页上按 F12 打开开发者工具 +2. 切换到 Console 标签 +3. 筛选显示 `[BlurText]` 日志 +4. 开启模糊模式后,应该看到: + ``` + [BlurText] Content script loaded + [BlurText] Received message: {action: "toggleBlurMode", ...} + [BlurText] Toggle blur mode: true intensity: 10 + [BlurText] Enabling blur mode + [BlurText] Event listeners attached + ``` + +### 查看 Popup 日志 + +1. 右键点击扩展图标 +2. 选择"检查弹出窗口" +3. 在打开的开发者工具中查看 Console +4. 点击按钮时应该看到对应的日志 + +### 测试点击事件 + +1. 开启模糊模式 +2. 点击网页任意元素 +3. Console 中应该显示: + ``` + [BlurText] Click detected, isBlurMode: true + [BlurText] Target element: DIV some-class + [BlurText] Adding blur to element, intensity: 10 + [BlurText] Total blurred elements: 1 + ``` + +### 检查 CSS 是否生效 + +1. 开启模糊模式后点击一个元素 +2. 右键该元素 → 检查 +3. 在 Elements 标签中,应该看到元素有 `blurtext-blurred` 类 +4. 在 Styles 中应该看到: + ```css + .blurtext-blurred { + filter: blur(10px); + ... + } + ``` + +## ✅ 确认安装成功的标志 + +1. ✅ `chrome://extensions/` 中看到 BlurText 扩展 +2. ✅ 扩展已启用(开关是蓝色的) +3. ✅ 工具栏显示紫色扩展图标 +4. ✅ 点击图标弹出精美的控制面板 +5. ✅ 打开 `test.html` 能看到测试页面 +6. ✅ 开启模糊模式后,页面顶部显示紫色提示条 +7. ✅ 鼠标悬停在文字上有蓝色虚线边框 +8. ✅ 点击文字后变模糊 + +## 📞 获取帮助 + +如果以上方法都无法解决问题: + +1. **检查浏览器版本**:确保使用最新版 Chrome/Edge +2. **尝试其他网页**:在多个网站测试 +3. **查看完整日志**:保存 Console 中的所有日志信息 +4. **重新安装**: + - 移除扩展 + - 关闭浏览器 + - 重新打开浏览器 + - 重新加载扩展 + +## 🎯 预期效果演示 + +### 正常工作流程 + +1. 点击扩展图标 → 弹出控制面板 ✅ +2. 点击"开启模糊模式" → 页面顶部显示提示 ✅ +3. 鼠标移到文字上 → 蓝色虚线边框 ✅ +4. 点击文字 → 变模糊 ✅ +5. 再次点击 → 取消模糊 ✅ +6. 按 ESC → 退出模糊模式 ✅ + +### 视觉效果 + +- **模糊前**: 文字清晰可读 +- **模糊后**: 文字变得模糊,像毛玻璃效果 +- **模糊强度**: 可通过滑块调节,范围 5px-20px + +--- + +**如果一切正常,恭喜你成功安装了 BlurText!🎉** diff --git a/README.md b/README.md new file mode 100644 index 0000000..b45033f --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# BlurText - 文字模糊保护插件 + +一款简单实用的浏览器扩展,专为屏幕分享和录制场景设计,帮助你快速模糊敏感文字信息,保护隐私安全。 + +## ✨ 功能特点 + +- 🖱️ **一键模糊**:点击即可模糊任意网页文字 +- 🎚️ **可调强度**:5-20px 模糊强度自由调节 +- 🔄 **即点即消**:再次点击取消模糊效果 +- 🗑️ **批量清除**:一键清除所有模糊效果 +- ⚡ **实时生效**:无需刷新,立即看到效果 +- 🔒 **本地处理**:所有数据本地存储,不上传云端 + +## 📦 安装方法 + +### Chrome / Edge / 其他 Chromium 浏览器 + +1. 下载本项目或克隆到本地 +2. 打开浏览器,进入扩展程序管理页面: + - Chrome: `chrome://extensions/` + - Edge: `edge://extensions/` +3. 开启右上角的"开发者模式" +4. 点击"加载已解压的扩展程序" +5. 选择本项目的文件夹 +6. 安装完成! + +## 🎯 使用方法 + +### 基础使用 + +1. 点击浏览器工具栏的 BlurText 图标 +2. 点击"开启模糊模式"按钮 +3. 在网页上点击要模糊的文字元素 +4. 再次点击已模糊的元素可取消模糊 +5. 按 `ESC` 键退出模糊模式 + +### 调整模糊强度 + +- 在弹出窗口中拖动"模糊强度"滑块 +- 范围:5px(轻微模糊)到 20px(强烈模糊) +- 实时生效,无需重新操作 + +### 清除所有模糊 + +- 点击弹出窗口中的"清除所有模糊"按钮 +- 所有模糊效果将立即移除 + +## 💡 使用场景 + +- **技术分享**:录制教程时隐藏 API Key、Token 等敏感信息 +- **在线会议**:演示时保护客户信息、内部数据 +- **直播带货**:展示后台时模糊订单、联系方式 +- **客服工作**:屏幕共享时保护用户隐私 +- **内容创作**:录制视频时快速处理敏感内容 + +## 🔧 技术栈 + +- **Manifest V3**:使用最新的浏览器扩展标准 +- **原生 JavaScript**:无框架依赖,轻量高效 +- **CSS Filter**:使用 CSS `filter: blur()` 实现模糊效果 +- **Chrome Storage API**:本地存储用户配置 + +## 📂 项目结构 + +``` +blurweb/ +├── manifest.json # 扩展配置文件 +├── popup.html # 弹出窗口界面 +├── popup.js # 弹出窗口逻辑 +├── content.js # 内容脚本(核心功能) +├── content.css # 注入样式 +├── icons/ # 图标文件夹 +│ ├── icon16.png +│ ├── icon32.png +│ ├── icon48.png +│ └── icon128.png +└── README.md # 说明文档 +``` + +## 🚀 后续计划 + +- [ ] 图片模糊功能 +- [ ] 区域模糊(矩形选择) +- [ ] 自动识别敏感信息(手机号、身份证等) +- [ ] 模糊记忆功能(页面刷新后保持) +- [ ] 导出/导入配置 +- [ ] 快捷键支持 +- [ ] Firefox 版本 + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request! + +## 📄 许可证 + +MIT License + +## ⚠️ 注意事项 + +1. 模糊效果仅在当前页面生效,刷新后需要重新操作 +2. 某些动态加载的内容可能需要重新模糊 +3. 模糊效果只是视觉隐藏,不影响网页的实际数据 +4. 建议在正式录制前测试模糊效果 + +## 📞 反馈 + +如有问题或建议,欢迎提交 Issue。 + +--- + +**开发初衷**:为了让屏幕分享和录制更安全、更便捷,不再需要后期打码,节省时间,保护隐私。 diff --git a/content.css b/content.css new file mode 100644 index 0000000..bbc7bbe --- /dev/null +++ b/content.css @@ -0,0 +1,113 @@ +/* content.css - 注入到网页的样式 */ + +/* 模糊效果类 */ +.blurtext-blurred { + filter: blur(var(--blur-intensity, 10px)) !important; + user-select: none !important; + pointer-events: auto !important; + cursor: pointer !important; + transition: filter 0.2s ease !important; + position: relative !important; +} + +/* 模糊元素的悬停效果 */ +.blurtext-blurred:hover::after { + content: '点击取消模糊' !important; + position: absolute !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + background: rgba(102, 126, 234, 0.95) !important; + color: white !important; + padding: 4px 8px !important; + border-radius: 4px !important; + font-size: 12px !important; + white-space: nowrap !important; + z-index: 999999 !important; + pointer-events: none !important; + filter: none !important; +} + +/* 模糊模式下的鼠标样式 */ +body.blurtext-mode * { + cursor: crosshair !important; +} + +/* 模糊模式提示 */ +.blurtext-hint { + position: fixed !important; + top: 20px !important; + left: 50% !important; + transform: translateX(-50%) !important; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; + color: white !important; + padding: 12px 24px !important; + border-radius: 24px !important; + font-size: 14px !important; + font-weight: 500 !important; + z-index: 9999999 !important; + box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4) !important; + animation: blurtext-slideDown 0.3s ease !important; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; +} + +@keyframes blurtext-slideDown { + from { + opacity: 0; + transform: translateX(-50%) translateY(-10px); + } + to { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +} + +/* 高亮效果(鼠标悬停在可模糊元素上) */ +.blurtext-highlight { + outline: 2px dashed #667eea !important; + outline-offset: 2px !important; + background: rgba(102, 126, 234, 0.1) !important; +} + +/* 文本选择模式的模糊按钮 */ +.blurtext-blur-button { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; + color: white !important; + padding: 10px 20px !important; + border-radius: 20px !important; + font-size: 14px !important; + font-weight: 500 !important; + cursor: pointer !important; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; + animation: blurtext-buttonPop 0.2s ease !important; + user-select: none !important; + white-space: nowrap !important; + border: none !important; + transition: all 0.3s ease !important; +} + +.blurtext-blur-button:hover { + transform: translateX(-50%) scale(1.05) !important; + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5) !important; +} + +.blurtext-blur-button:active { + transform: translateX(-50%) scale(0.95) !important; +} + +@keyframes blurtext-buttonPop { + from { + opacity: 0; + transform: translateX(-50%) scale(0.8); + } + to { + opacity: 1; + transform: translateX(-50%) scale(1); + } +} + +/* 文本选择模式包裹的元素 */ +.blurtext-selection-wrapped { + display: inline !important; +} diff --git a/content.js b/content.js new file mode 100644 index 0000000..fde1276 --- /dev/null +++ b/content.js @@ -0,0 +1,372 @@ +// content.js - 核心功能实现 +(function() { + 'use strict'; + + console.log('[BlurText] Content script loaded'); + + let isBlurMode = false; + let blurMode = 'element'; // 'element' or 'selection' + let blurIntensity = 10; + let blurredElements = new Set(); + let hintElement = null; + let blurButton = null; // 浮动模糊按钮 + + // 初始化:从存储中加载配置 + chrome.storage.local.get(['blurIntensity'], (result) => { + if (result.blurIntensity) { + blurIntensity = result.blurIntensity; + console.log('[BlurText] Loaded blur intensity:', blurIntensity); + } + }); + + // 监听来自 popup 的消息 + chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + console.log('[BlurText] Received message:', request); + + if (request.action === 'toggleBlurMode') { + isBlurMode = request.enabled; + blurMode = request.mode || 'element'; + blurIntensity = request.intensity || 10; + + console.log('[BlurText] Toggle blur mode:', isBlurMode, 'mode:', blurMode, 'intensity:', blurIntensity); + + if (isBlurMode) { + enableBlurMode(); + } else { + disableBlurMode(); + } + } else if (request.action === 'clearAll') { + console.log('[BlurText] Clear all blurs'); + clearAllBlurs(); + } else if (request.action === 'updateIntensity') { + blurIntensity = request.intensity; + console.log('[BlurText] Update intensity:', blurIntensity); + updateAllBlurIntensity(); + } else if (request.action === 'switchMode') { + blurMode = request.mode; + console.log('[BlurText] Switch mode:', blurMode); + updateModeUI(); + } + }); + + // 启用模糊模式 + function enableBlurMode() { + console.log('[BlurText] Enabling blur mode, mode:', blurMode); + + const modeText = blurMode === 'selection' ? '文本选择模式 - 拖动选择文字后点击模糊按钮' : '元素模式 - 点击元素进行模糊'; + showHint(`模糊模式已开启 - ${modeText},按 ESC 退出`); + + // 根据模式添加对应的样式和事件 + if (blurMode === 'element') { + document.body.classList.add('blurtext-mode'); + document.addEventListener('click', handleClick, true); + document.addEventListener('mouseover', handleMouseOver, true); + document.addEventListener('mouseout', handleMouseOut, true); + } else if (blurMode === 'selection') { + // 文本选择模式不需要 crosshair 光标 + document.addEventListener('mouseup', handleTextSelection, true); + } + + // 键盘事件对两种模式都需要 + document.addEventListener('keydown', handleKeydown, true); + + console.log('[BlurText] Event listeners attached'); + } + + // 禁用模糊模式 + function disableBlurMode() { + document.body.classList.remove('blurtext-mode'); + hideHint(); + hideBlurButton(); + + // 移除所有事件监听 + document.removeEventListener('click', handleClick, true); + document.removeEventListener('keydown', handleKeydown, true); + document.removeEventListener('mouseover', handleMouseOver, true); + document.removeEventListener('mouseout', handleMouseOut, true); + document.removeEventListener('mouseup', handleTextSelection, true); + + // 清除所有高亮和预览 + removeAllHighlights(); + } + + // 更新模式UI + function updateModeUI() { + if (!isBlurMode) return; + + console.log('[BlurText] Switching mode to:', blurMode); + + // 先移除所有旧的事件监听和样式 + document.body.classList.remove('blurtext-mode'); + document.removeEventListener('click', handleClick, true); + document.removeEventListener('mouseover', handleMouseOver, true); + document.removeEventListener('mouseout', handleMouseOut, true); + document.removeEventListener('mouseup', handleTextSelection, true); + + // 清除旧模式的UI元素 + hideBlurButton(); + removeAllHighlights(); + + // 根据新模式添加事件监听和样式 + if (blurMode === 'element') { + document.body.classList.add('blurtext-mode'); + document.addEventListener('click', handleClick, true); + document.addEventListener('mouseover', handleMouseOver, true); + document.addEventListener('mouseout', handleMouseOut, true); + showHint('已切换到元素模式 - 点击元素进行模糊', 3000); + } else if (blurMode === 'selection') { + // 文本选择模式不需要 crosshair 光标 + document.addEventListener('mouseup', handleTextSelection, true); + showHint('已切换到文本选择模式 - 拖动选择文字后点击模糊按钮', 3000); + } + + console.log('[BlurText] Mode switched successfully'); + } + + // 处理点击事件 + function handleClick(e) { + console.log('[BlurText] Click detected, isBlurMode:', isBlurMode, 'blurMode:', blurMode); + + if (!isBlurMode || blurMode !== 'element') return; + + // 阻止默认行为 + e.preventDefault(); + e.stopPropagation(); + + let target = e.target; + console.log('[BlurText] Target element:', target.tagName, target.className, 'text:', target.textContent?.substring(0, 20)); + + // 如果是提示元素,忽略 + if (target.classList.contains('blurtext-hint')) { + console.log('[BlurText] Ignored hint element'); + return; + } + + // 切换模糊状态 + toggleBlur(target); + } + + // 处理键盘事件(ESC 退出) + function handleKeydown(e) { + if (e.key === 'Escape' && isBlurMode) { + isBlurMode = false; + disableBlurMode(); + + // 通知 popup + chrome.runtime.sendMessage({ action: 'blurModeDisabled' }); + } + } + + // 处理文本选择(文本选择模式) + function handleTextSelection(e) { + if (!isBlurMode || blurMode !== 'selection') return; + + // 避免点击模糊按钮时触发 + if (e.target.classList.contains('blurtext-blur-button')) { + return; + } + + const selection = window.getSelection(); + const selectedText = selection.toString().trim(); + + console.log('[BlurText] Text selection:', selectedText); + + if (selectedText.length > 0) { + // 显示模糊按钮 + showBlurButton(selection); + } else { + // 没有选中文本,隐藏按钮 + hideBlurButton(); + } + } + + // 显示模糊按钮 + function showBlurButton(selection) { + // 移除旧按钮 + hideBlurButton(); + + // 获取选区的位置 + const range = selection.getRangeAt(0); + const rect = range.getBoundingClientRect(); + + // 创建模糊按钮 + blurButton = document.createElement('div'); + blurButton.className = 'blurtext-blur-button'; + blurButton.textContent = '🔒 模糊选中文本'; + blurButton.style.position = 'fixed'; + blurButton.style.left = `${rect.left + rect.width / 2}px`; + blurButton.style.top = `${rect.bottom + 10}px`; + blurButton.style.transform = 'translateX(-50%)'; + blurButton.style.zIndex = '2147483647'; + + // 点击按钮模糊文本 + blurButton.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + blurSelectedText(); + }); + + document.body.appendChild(blurButton); + } + + // 隐藏模糊按钮 + function hideBlurButton() { + if (blurButton && blurButton.parentNode) { + blurButton.parentNode.removeChild(blurButton); + blurButton = null; + } + } + + // 模糊选中的文本 + function blurSelectedText() { + const selection = window.getSelection(); + if (!selection.rangeCount) return; + + try { + const range = selection.getRangeAt(0); + + // 创建 span 包裹选中的文本 + const span = document.createElement('span'); + span.className = 'blurtext-blurred blurtext-selection-wrapped'; + span.style.setProperty('--blur-intensity', `${blurIntensity}px`); + + // 包裹选中的内容 + range.surroundContents(span); + + // 添加到模糊元素集合 + blurredElements.add(span); + + console.log('[BlurText] Text selection blurred, total elements:', blurredElements.size); + + // 清除选择 + selection.removeAllRanges(); + + // 隐藏按钮 + hideBlurButton(); + + // 显示提示 + showHint('文本已模糊', 2000); + } catch (error) { + console.error('[BlurText] Error blurring selection:', error); + showHint('无法模糊该选区(可能包含复杂的HTML结构)', 3000); + hideBlurButton(); + } + } + + // 鼠标悬停高亮 + function handleMouseOver(e) { + if (!isBlurMode || blurMode !== 'element') return; + + const target = e.target; + if (target.classList.contains('blurtext-hint') || + target.classList.contains('blurtext-blurred')) { + return; + } + + // 移除之前的高亮 + removeAllHighlights(); + + // 直接高亮元素 + target.classList.add('blurtext-highlight'); + } + + // 移除所有高亮 + function removeAllHighlights() { + const highlighted = document.querySelectorAll('.blurtext-highlight'); + highlighted.forEach(el => { + el.classList.remove('blurtext-highlight'); + }); + } + + // 鼠标移出取消高亮 + function handleMouseOut(e) { + if (!isBlurMode || blurMode !== 'element') return; + + // 延迟移除,避免在元素间移动时闪烁 + setTimeout(() => { + // 检查鼠标是否还在模糊模式下的元素上 + const hoveredElement = document.elementFromPoint(e.clientX, e.clientY); + if (!hoveredElement || + hoveredElement.classList.contains('blurtext-hint') || + hoveredElement.classList.contains('blurtext-blurred')) { + return; + } + + // 如果鼠标不在任何高亮元素上,清除所有高亮 + const highlighted = document.querySelector('.blurtext-highlight:hover'); + if (!highlighted) { + removeAllHighlights(); + } + }, 100); + } + + // 切换元素模糊状态 + function toggleBlur(element) { + if (blurredElements.has(element)) { + // 取消模糊 + console.log('[BlurText] Removing blur from element'); + element.classList.remove('blurtext-blurred'); + element.style.removeProperty('--blur-intensity'); + blurredElements.delete(element); + } else { + // 添加模糊 + console.log('[BlurText] Adding blur to element, intensity:', blurIntensity); + element.classList.add('blurtext-blurred'); + element.style.setProperty('--blur-intensity', `${blurIntensity}px`); + blurredElements.add(element); + } + console.log('[BlurText] Total blurred elements:', blurredElements.size); + } + + // 清除所有模糊 + function clearAllBlurs() { + blurredElements.forEach(element => { + element.classList.remove('blurtext-blurred'); + element.style.removeProperty('--blur-intensity'); + }); + blurredElements.clear(); + + // 如果在模糊模式下,显示提示 + if (isBlurMode) { + showHint('已清除所有模糊效果', 2000); + } + } + + // 更新所有已模糊元素的强度 + function updateAllBlurIntensity() { + blurredElements.forEach(element => { + element.style.setProperty('--blur-intensity', `${blurIntensity}px`); + }); + } + + // 显示提示 + function showHint(message, duration = null) { + // 移除旧提示 + hideHint(); + + // 创建新提示 + hintElement = document.createElement('div'); + hintElement.className = 'blurtext-hint'; + hintElement.textContent = message; + document.body.appendChild(hintElement); + + // 如果指定了持续时间,自动隐藏 + if (duration) { + setTimeout(() => { + hideHint(); + }, duration); + } + } + + // 隐藏提示 + function hideHint() { + if (hintElement && hintElement.parentNode) { + hintElement.parentNode.removeChild(hintElement); + hintElement = null; + } + } + + // 页面加载完成后,恢复之前的模糊状态(未来功能) + // 这里可以添加持久化存储功能 + +})(); diff --git a/icon-generator.html b/icon-generator.html new file mode 100644 index 0000000..0a9da09 --- /dev/null +++ b/icon-generator.html @@ -0,0 +1,173 @@ + + +
+ + +右键点击图标 → 另存为图像 → 保存到 icons 文件夹
+ +icons/ 文件夹,文件名分别为 icon16.png, icon32.png, icon48.png, icon128.png注意:图标文件必须保存在 icons/ 文件夹中,并使用正确的文件名。
屏幕分享隐私保护
+ +测试 BlurText 自动处理纯文本节点的能力
+ +<span> 中,然后模糊。
+ 姓名:李四
+年龄:28
+城市:上海
+💡 测试方法:
+手机号:138-8888-9999
+邮箱:user@example.com
+API Key: sk-1234567890abcdefghijk
+💡 测试方法:
+用户 张三 的手机号是 186-0000-1234,邮箱是 zhangsan@company.com
+💡 测试方法:
+💡 测试方法:
+document.caretRangeFromPoint() 检测是否点在文本节点上/\d{3,}[-\d]*//[\w\.-]+@[\w\.-]+\.\w+//[a-zA-Z0-9_-]{10,}/<span> 包裹该部分<span> 应用模糊效果
+ [BlurText] Detected text node: ...[BlurText] Found sensitive value: ...[BlurText] Wrapped text node in span
+ 使用这个页面测试模糊功能是否正常工作
+ +ESC 键退出模糊模式这是一段普通的文本内容,你可以点击这段文字来测试模糊功能。
+模糊功能应该可以立即生效,不需要屏幕录制或分享。
+姓名:张三
+身份证:110101199001011234
+银行卡:6222 0000 1111 2222
+订单号:ORD-2025-001234
+金额:¥1,288.00
+地址:北京市朝阳区某某街道123号
+如果功能不工作,请检查:
+chrome://extensions/)<span>)。
+ 姓名:张三
+<p>
+ <strong>姓名:</strong>
+ <span>张三</span>
+</p>
+ ✅ 效果:
+<strong> 标签<span> 标签<p><div class="sensitive-info">
+ <strong>手机号:</strong>
+ <span>138-0000-1234</span>
+</div>
+ ✅ 效果:
+姓名:张三
+<p>
+ <strong>姓名:</strong>
+ 张三
+</p>
+ ❌ 问题:
+<p><p>(包括"姓名:")// 错误 ❌
+<p>手机号:138-0000-1234</p>
+
+// 正确 ✅
+<p>手机号:<span>138-0000-1234</span></p>
+ <!-- 表单字段 -->
+<div class="field">
+ <label>姓名</label>
+ <span class="value">张三</span>
+</div>
+
+<!-- 列表项 -->
+<ul>
+ <li>
+ <strong>API Key:</strong>
+ <code>sk-1234567890abcdef</code>
+ </li>
+</ul>
+
+<!-- 表格 -->
+<table>
+ <tr>
+ <td>手机号</td>
+ <td><span>138-0000-1234</span></td>
+ </tr>
+</table>
+ <span> 或其他标签中打开 test.html 测试页面,我已经更新了 HTML 结构,现在可以:
+