first commit

This commit is contained in:
ytc1012
2025-12-10 18:18:30 +08:00
commit 760f3ec0d0
18 changed files with 2588 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(wc:*)"
]
}
}

227
CHANGELOG.md Normal file
View File

@@ -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
<p>用户张三的手机号是138-8888-9999邮箱是zhang@example.com</p>
```
**元素模式**
- 点击 → 模糊整个段落 ❌
**文本选择模式**
- 选择"138-8888-9999" → 只模糊手机号 ✅
- 选择"zhang@example.com" → 只模糊邮箱 ✅
#### **场景 2表格数据**
```html
<table>
<tr><td>姓名</td><td>李四</td></tr>
<tr><td>工号</td><td>E12345</td></tr>
</table>
```
**元素模式**
- 点击第二列的 td → 精确模糊单元格 ✅
**文本选择模式**
- 也可以选择文本模糊,但元素模式更快捷
---
### 📊 代码变更统计
| 文件 | 变更类型 | 行数变化 |
|------|---------|---------|
| content.js | 新增功能 | +100 行 |
| popup.html | UI 更新 | +30 行 |
| popup.js | 逻辑扩展 | +60 行 |
| content.css | 样式新增 | +43 行 |
| **总计** | | **+233 行** |
---
### 🐛 已修复问题
1.**popup 关闭问题**
- 说明:这是浏览器扩展的默认行为,不是 bug
- 正常使用开启模糊模式后popup 会自动关闭
2.**纯文本节点无法模糊**
- 通过文本选择模式完美解决
- 不再需要复杂的文本节点检测
---
### 🔍 技术亮点
#### **1. Selection API 的正确使用**
- 使用 `window.getSelection()` 获取选区
- 使用 `range.surroundContents()` 包裹文本
- 正确处理 `surroundContents` 异常
#### **2. 模式切换机制**
- 动态添加/移除事件监听器
- 根据模式显示不同的 UI 提示
- 实时切换无需刷新
#### **3. 用户体验优化**
- 浮动按钮自动定位
- 平滑动画过渡
- 友好的错误提示
---
### 🚀 性能优化
- 事件监听器按需添加/移除
- 避免不必要的 DOM 查询
- CSS 动画使用 GPU 加速transform
---
### 📚 参考资料
本次更新参考了以下浏览器扩展的实现:
- [blurweb.app](https://www.blurweb.app/) - 文本选择模式设计
- [Selection API](https://developer.mozilla.org/en-US/docs/Web/API/Selection) - Mozilla 文档
---
### 🔮 后续计划
1. **区域框选模式** - 拖拽绘制矩形模糊区域
2. **模糊记忆** - 页面刷新后保持模糊状态
3. **快捷键支持** - Ctrl+B 快速切换模式
4. **批量操作** - 一次选择多个区域
---
## v1.0.0 (2025-01-09) - 初始版本
### 功能
- ✅ 元素点击模糊
- ✅ 模糊强度调节
- ✅ 批量清除模糊
- ✅ ESC 键退出
---
## 升级建议
### 从 v1.0.0 升级到 v2.0.0
1. 刷新扩展chrome://extensions/
2. 重新加载需要使用的网页
3. 尝试新的文本选择模式
**兼容性**
- ✅ 完全向后兼容
- ✅ 元素模式保持不变
- ✅ 无需修改使用习惯

204
FIXES.md Normal file
View File

@@ -0,0 +1,204 @@
# BlurText 修复说明
## 🔄 重大变更2025-01-10
### 移除文本节点模糊功能
**决策原因**
文本节点自动检测和包裹功能虽然看起来智能但引入了过多复杂性和bug
1. 预览高亮和实际模糊的交互逻辑复杂
2. DOM 操作频繁,容易出现元素消失的问题
3. 预览 span 的生命周期管理困难
4. 用户体验不够稳定
**回归简单模式**
- ✅ 只模糊 HTML 元素span, div, p, td 等)
- ✅ 鼠标悬停高亮,点击即模糊
- ✅ 稳定可靠,不会出现元素消失的问题
- ❌ 不再自动检测和包裹纯文本节点
- ❌ 不再智能识别敏感信息(手机号、邮箱等)
**使用建议**
- 如果需要模糊纯文本,可以先手动用 `<span>` 包裹
- 或者点击包含该文本的父元素进行整体模糊
---
## 🐛 已修复的问题
### 问题 1Popup 卡片在点击页面后消失
**症状**
- 开启模糊模式后,点击扩展图标打开 popup
- 点击网页上的文字进行模糊
- Popup 卡片立即消失
**原因**
这是浏览器扩展的**默认行为**,不是 bug
- 当用户点击 popup 外部的任何区域(包括网页内容)时,浏览器会自动关闭 popup
- 这是 Chrome/Edge 扩展的标准行为,无法通过配置改变
**解决方案**
1. **正常使用流程**
- 点击扩展图标 → 开启模糊模式 → Popup 自动关闭
- 直接在网页上点击元素进行模糊
- 需要调整强度或关闭模式时,再次点击扩展图标打开 popup
2. **替代方案**(未来考虑):
- 添加侧边栏模式(使用 `chrome.sidePanel` API需要 Manifest V3 + Chrome 114+
- 添加快捷键支持(无需打开 popup 即可切换模式)
---
## ✅ 当前功能
### 基础模糊功能
```html
<div>
<span>用户名</span>
<span>张三</span>
</div>
```
**使用方法**
1. 开启模糊模式
2. 鼠标悬停在元素上 → 显示蓝色高亮边框
3. 点击元素 → 应用模糊效果
4. 再次点击 → 取消模糊
**特点**
- ✅ 悬停高亮的元素 = 点击后模糊的元素(所见即所得)
- ✅ 可以模糊任何 HTML 元素
- ✅ 可调节模糊强度5-20px
- ✅ 支持批量清除所有模糊
- ✅ ESC 键退出模糊模式
---
## 🎯 适用场景
### 场景 1结构化数据
```html
<table>
<tr>
<td>姓名</td>
<td>张三</td>
</tr>
</table>
```
- 鼠标悬停第二个 `<td>` → 高亮
- 点击 → 模糊 ✅
### 场景 2带标签的元素
```html
<p>
<strong>手机号:</strong>
<span>138-8888-9999</span>
</p>
```
- 鼠标悬停 `<span>` → 高亮手机号
- 点击 → 模糊手机号 ✅
### 场景 3纯文本需要手动处理
```html
<p>用户 张三 的手机号是 138-8888-9999</p>
```
- 悬停任意位置 → 高亮整个 `<p>`
- 点击 → 模糊整段文字
- **如需只模糊"张三"**:先手动编辑 HTML`<span>张三</span>` 包裹
---
## 🔧 技术实现
### 简化后的核心逻辑
**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 操作复杂,容易出问题)
- ❌ 临时元素管理(生命周期难以控制)
- ❌ 智能识别(正则匹配不够可靠)
- ✅ 让用户明确点击目标元素(简单直接)

186
INSTALL.md Normal file
View File

@@ -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🎉**

111
README.md Normal file
View File

@@ -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。
---
**开发初衷**:为了让屏幕分享和录制更安全、更便捷,不再需要后期打码,节省时间,保护隐私。

113
content.css Normal file
View File

@@ -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;
}

372
content.js Normal file
View File

@@ -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;
}
}
// 页面加载完成后,恢复之前的模糊状态(未来功能)
// 这里可以添加持久化存储功能
})();

173
icon-generator.html Normal file
View File

@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlurText 图标生成器</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 40px;
background: #f5f5f5;
text-align: center;
}
.icon-container {
display: inline-flex;
gap: 30px;
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
margin: 20px 0;
}
.icon-item {
text-align: center;
}
svg {
display: block;
margin-bottom: 10px;
}
button {
margin-top: 10px;
padding: 8px 16px;
background: #667eea;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #5568d3;
}
.instructions {
max-width: 600px;
margin: 20px auto;
background: white;
padding: 20px;
border-radius: 8px;
text-align: left;
}
h1 {
color: #667eea;
}
</style>
</head>
<body>
<h1>🔒 BlurText 图标生成器</h1>
<p>右键点击图标 → 另存为图像 → 保存到 icons 文件夹</p>
<div class="icon-container">
<div class="icon-item">
<svg id="icon16" width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad16" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="16" height="16" rx="3" fill="url(#grad16)"/>
<path d="M4 5h8M4 8h6M4 11h7" stroke="white" stroke-width="1.5" stroke-linecap="round" opacity="0.6" filter="blur(0.5)"/>
<circle cx="12" cy="12" r="2.5" fill="white" opacity="0.9"/>
<path d="M10.5 12h3" stroke="#667eea" stroke-width="1.2" stroke-linecap="round"/>
</svg>
<div>16x16</div>
<button onclick="downloadSVG('icon16', 16)">下载</button>
</div>
<div class="icon-item">
<svg id="icon32" width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad32" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="32" height="32" rx="6" fill="url(#grad32)"/>
<path d="M6 8h20M6 14h16M6 20h18" stroke="white" stroke-width="2.5" stroke-linecap="round" opacity="0.6" filter="blur(1)"/>
<circle cx="24" cy="24" r="5" fill="white" opacity="0.95"/>
<path d="M21 24h6" stroke="#667eea" stroke-width="2" stroke-linecap="round"/>
</svg>
<div>32x32</div>
<button onclick="downloadSVG('icon32', 32)">下载</button>
</div>
<div class="icon-item">
<svg id="icon48" width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad48" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="48" height="48" rx="10" fill="url(#grad48)"/>
<path d="M10 12h28M10 20h24M10 28h26M10 36h22" stroke="white" stroke-width="3" stroke-linecap="round" opacity="0.6" filter="blur(1.5)"/>
<circle cx="36" cy="36" r="7" fill="white" opacity="0.95"/>
<path d="M32 36h8" stroke="#667eea" stroke-width="2.5" stroke-linecap="round"/>
</svg>
<div>48x48</div>
<button onclick="downloadSVG('icon48', 48)">下载</button>
</div>
<div class="icon-item">
<svg id="icon128" width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad128" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="128" height="128" rx="24" fill="url(#grad128)"/>
<path d="M24 28h80M24 48h70M24 68h75M24 88h65M24 108h72" stroke="white" stroke-width="6" stroke-linecap="round" opacity="0.6" filter="blur(3)"/>
<circle cx="96" cy="96" r="18" fill="white" opacity="0.95"/>
<path d="M86 96h20" stroke="#667eea" stroke-width="5" stroke-linecap="round"/>
</svg>
<div>128x128</div>
<button onclick="downloadSVG('icon128', 128)">下载</button>
</div>
</div>
<div class="instructions">
<h3>📝 使用说明</h3>
<ol>
<li><strong>方法一(推荐):</strong>右键点击每个图标 → 另存为图像 → 保存到 <code>icons/</code> 文件夹,文件名分别为 <code>icon16.png</code>, <code>icon32.png</code>, <code>icon48.png</code>, <code>icon128.png</code></li>
<li><strong>方法二:</strong>点击每个图标下方的"下载"按钮,自动下载 PNG 文件</li>
<li><strong>方法三:</strong>使用在线工具将 SVG 转换为 PNG
<ul>
<li>访问 <a href="https://cloudconvert.com/svg-to-png" target="_blank">CloudConvert</a></li>
<li>上传 SVG 图标,设置对应尺寸,转换为 PNG</li>
</ul>
</li>
</ol>
<p><strong>注意:</strong>图标文件必须保存在 <code>icons/</code> 文件夹中,并使用正确的文件名。</p>
</div>
<script>
function downloadSVG(id, size) {
const svg = document.getElementById(id);
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
const data = new XMLSerializer().serializeToString(svg);
const img = new Image();
const blob = new Blob([data], {type: 'image/svg+xml'});
const url = URL.createObjectURL(blob);
img.onload = function() {
ctx.drawImage(img, 0, 0);
canvas.toBlob(function(blob) {
const link = document.createElement('a');
link.download = `${id}.png`;
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(url);
});
};
img.src = url;
}
</script>
</body>
</html>

BIN
icons/icon128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
icons/icon16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

BIN
icons/icon32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
icons/icon48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

33
manifest.json Normal file
View File

@@ -0,0 +1,33 @@
{
"manifest_version": 3,
"name": "BlurText - 文字模糊保护",
"version": "1.0.0",
"description": "屏幕分享和录制时一键模糊敏感文字信息,保护隐私安全",
"permissions": [
"activeTab",
"storage"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": ["content.css"],
"run_at": "document_end"
}
],
"icons": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}

282
popup.html Normal file
View File

@@ -0,0 +1,282 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlurText</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 320px;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
}
.container {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}
h1 {
font-size: 20px;
margin-bottom: 8px;
color: #667eea;
text-align: center;
}
.subtitle {
font-size: 12px;
color: #888;
text-align: center;
margin-bottom: 20px;
}
.status {
background: #f0f4ff;
border-left: 4px solid #667eea;
padding: 12px;
margin-bottom: 20px;
border-radius: 4px;
font-size: 13px;
color: #555;
}
.status.active {
background: #e8f5e9;
border-left-color: #4caf50;
}
.controls {
display: flex;
flex-direction: column;
gap: 12px;
}
button {
padding: 12px 20px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
button:active {
transform: translateY(0);
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-secondary {
background: #f5f5f5;
color: #666;
}
.btn-secondary:hover {
background: #e0e0e0;
}
.intensity-control {
margin-bottom: 20px;
}
.intensity-control label {
display: block;
font-size: 13px;
color: #666;
margin-bottom: 8px;
font-weight: 500;
}
.intensity-slider {
display: flex;
align-items: center;
gap: 12px;
}
input[type="range"] {
flex: 1;
height: 6px;
border-radius: 3px;
background: #e0e0e0;
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
border: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.intensity-value {
font-size: 14px;
font-weight: 600;
color: #667eea;
min-width: 35px;
text-align: center;
}
.mode-selector {
margin-bottom: 20px;
}
.mode-selector label {
display: block;
font-size: 13px;
color: #666;
margin-bottom: 8px;
font-weight: 500;
}
.mode-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.mode-btn {
padding: 10px;
border: 2px solid #e0e0e0;
background: white;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.mode-btn:hover {
border-color: #667eea;
transform: none;
box-shadow: none;
}
.mode-btn.active {
border-color: #667eea;
background: #f0f4ff;
color: #667eea;
}
.mode-btn .mode-icon {
font-size: 20px;
}
.mode-btn .mode-label {
font-size: 12px;
font-weight: 500;
}
.usage-tip {
background: #fff9e6;
border-left: 4px solid #ffc107;
padding: 10px;
margin-top: 20px;
border-radius: 4px;
font-size: 12px;
color: #666;
line-height: 1.5;
}
.icon {
font-size: 16px;
}
</style>
</head>
<body>
<div class="container">
<h1>🔒 BlurText</h1>
<p class="subtitle">屏幕分享隐私保护</p>
<div class="status" id="status">
<span class="icon">💡</span> 点击下方按钮开始使用
</div>
<div class="mode-selector">
<label>模糊模式</label>
<div class="mode-buttons">
<button class="mode-btn active" id="modeElement" data-mode="element">
<span class="mode-icon">🖱️</span>
<span class="mode-label">元素模式</span>
</button>
<button class="mode-btn" id="modeSelection" data-mode="selection">
<span class="mode-icon">📝</span>
<span class="mode-label">文本选择</span>
</button>
</div>
</div>
<div class="intensity-control">
<label for="blurIntensity">模糊强度</label>
<div class="intensity-slider">
<input type="range" id="blurIntensity" min="5" max="20" value="10" step="1">
<span class="intensity-value" id="intensityValue">10px</span>
</div>
</div>
<div class="controls">
<button class="btn-primary" id="toggleBlur">
<span class="icon">🖱️</span>
<span id="toggleText">开启模糊模式</span>
</button>
<button class="btn-secondary" id="clearAll">
<span class="icon">🗑️</span>
清除所有模糊
</button>
</div>
<div class="usage-tip">
<strong>使用方法:</strong><br>
<span id="usageTip">
<strong>元素模式:</strong><br>
1. 点击"开启模糊模式"<br>
2. 鼠标悬停元素查看高亮<br>
3. 点击元素进行模糊<br>
4. 再次点击取消模糊
</span>
</div>
</div>
<script src="popup.js"></script>
</body>
</html>

168
popup.js Normal file
View File

@@ -0,0 +1,168 @@
// popup.js - 弹出窗口逻辑
let isBlurMode = false;
let currentMode = 'element'; // 'element' or 'selection'
console.log('[BlurText] Popup script loaded');
// 获取页面元素
const toggleBtn = document.getElementById('toggleBlur');
const toggleText = document.getElementById('toggleText');
const clearAllBtn = document.getElementById('clearAll');
const blurIntensitySlider = document.getElementById('blurIntensity');
const intensityValue = document.getElementById('intensityValue');
const statusDiv = document.getElementById('status');
const modeElementBtn = document.getElementById('modeElement');
const modeSelectionBtn = document.getElementById('modeSelection');
const usageTip = document.getElementById('usageTip');
// 从存储中加载模糊强度
chrome.storage.local.get(['blurIntensity'], (result) => {
if (result.blurIntensity) {
blurIntensitySlider.value = result.blurIntensity;
intensityValue.textContent = result.blurIntensity + 'px';
}
});
// 模糊强度滑块变化
blurIntensitySlider.addEventListener('input', (e) => {
const value = e.target.value;
intensityValue.textContent = value + 'px';
// 保存到存储
chrome.storage.local.set({ blurIntensity: value });
// 通知 content script 更新强度
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {
action: 'updateIntensity',
intensity: value
});
});
});
// 模式切换
[modeElementBtn, modeSelectionBtn].forEach(btn => {
btn.addEventListener('click', () => {
const mode = btn.dataset.mode;
currentMode = mode;
// 更新UI
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// 更新使用提示
updateUsageTip(mode);
// 如果已经在模糊模式中,通知 content script 切换模式
if (isBlurMode) {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {
action: 'switchMode',
mode: mode
});
});
}
});
});
// 更新使用提示
function updateUsageTip(mode) {
if (mode === 'element') {
usageTip.innerHTML = `
<strong>元素模式:</strong><br>
1. 点击"开启模糊模式"<br>
2. 鼠标悬停元素查看高亮<br>
3. 点击元素进行模糊<br>
4. 再次点击取消模糊
`;
} else {
usageTip.innerHTML = `
<strong>文本选择模式:</strong><br>
1. 点击"开启模糊模式"<br>
2. 拖动鼠标选择文本<br>
3. 点击浮动按钮模糊文本<br>
4. 可多次选择和模糊
`;
}
}
// 切换模糊模式
toggleBtn.addEventListener('click', async () => {
try {
console.log('[BlurText] Toggle button clicked');
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab) {
console.error('[BlurText] No active tab found');
statusDiv.innerHTML = '<span class="icon">⚠️</span> 无法获取当前标签页';
return;
}
isBlurMode = !isBlurMode;
console.log('[BlurText] New blur mode state:', isBlurMode);
// 发送消息给 content script
chrome.tabs.sendMessage(tab.id, {
action: 'toggleBlurMode',
enabled: isBlurMode,
mode: currentMode,
intensity: blurIntensitySlider.value
}, (response) => {
if (chrome.runtime.lastError) {
console.error('[BlurText] Error sending message:', chrome.runtime.lastError);
statusDiv.innerHTML = '<span class="icon">⚠️</span> 请刷新页面后重试';
statusDiv.className = 'status';
isBlurMode = !isBlurMode; // 恢复状态
} else {
console.log('[BlurText] Message sent successfully');
}
});
// 更新 UI
updateUI();
} catch (error) {
console.error('[BlurText] Error in toggle:', error);
statusDiv.innerHTML = '<span class="icon">❌</span> 发生错误,请重试';
}
});
// 清除所有模糊
clearAllBtn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.sendMessage(tab.id, {
action: 'clearAll'
});
statusDiv.innerHTML = '<span class="icon">✅</span> 已清除所有模糊效果';
statusDiv.className = 'status';
setTimeout(() => {
statusDiv.innerHTML = '<span class="icon">💡</span> 点击下方按钮开始使用';
}, 2000);
});
// 更新 UI 状态
function updateUI() {
if (isBlurMode) {
toggleText.textContent = '关闭模糊模式';
toggleBtn.classList.remove('btn-primary');
toggleBtn.classList.add('btn-secondary');
statusDiv.innerHTML = '<span class="icon">✨</span> 模糊模式已开启,点击网页文字进行模糊';
statusDiv.className = 'status active';
} else {
toggleText.textContent = '开启模糊模式';
toggleBtn.classList.add('btn-primary');
toggleBtn.classList.remove('btn-secondary');
statusDiv.innerHTML = '<span class="icon">💡</span> 点击下方按钮开始使用';
statusDiv.className = 'status';
}
}
// 监听来自 content script 的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'blurModeDisabled') {
isBlurMode = false;
updateUI();
}
});

205
test-textnode.html Normal file
View File

@@ -0,0 +1,205 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>纯文本节点测试</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
max-width: 800px;
margin: 40px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
h1 {
color: #667eea;
margin-bottom: 10px;
}
.subtitle {
color: #888;
margin-bottom: 30px;
}
.test-box {
background: #f8f9fa;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #667eea;
border-radius: 4px;
}
.test-box h3 {
margin-top: 0;
color: #764ba2;
}
.example {
background: white;
padding: 15px;
margin: 10px 0;
border: 2px solid #ddd;
border-radius: 8px;
}
.badge {
display: inline-block;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
margin-left: 10px;
}
.badge-auto {
background: #4caf50;
color: white;
}
code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
color: #e91e63;
}
.info {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
.success {
background: #e8f5e9;
border-left: 4px solid #4caf50;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
strong {
color: #333;
}
</style>
</head>
<body>
<div class="container">
<h1>🧪 纯文本节点测试</h1>
<p class="subtitle">测试 BlurText 自动处理纯文本节点的能力</p>
<div class="success">
<strong>✨ 新功能:</strong>现在 BlurText 可以自动处理纯文本节点!<br>
当你点击纯文本时,扩展会自动将其包裹在 <code>&lt;span&gt;</code> 中,然后模糊。
</div>
<div class="test-box">
<h3>测试 1纯文本节点自动处理<span class="badge badge-auto">智能</span></h3>
<div class="example">
<p><strong>姓名:</strong>李四</p>
<p><strong>年龄:</strong>28</p>
<p><strong>城市:</strong>上海</p>
</div>
<p>💡 <strong>测试方法:</strong></p>
<ul>
<li>点击"李四" → 应该<strong>自动识别并只模糊"李四"</strong></li>
<li>点击"28" → 应该只模糊数字</li>
<li>点击"上海" → 应该只模糊城市名</li>
<li>点击"姓名:"标签 → 模糊标签</li>
</ul>
</div>
<div class="test-box">
<h3>测试 2混合文本智能分割<span class="badge badge-auto">智能</span></h3>
<div class="example">
<p>手机号138-8888-9999</p>
<p>邮箱user@example.com</p>
<p>API Key: sk-1234567890abcdefghijk</p>
</div>
<p>💡 <strong>测试方法:</strong></p>
<ul>
<li>点击"138-8888-9999" → <strong>自动识别手机号并单独模糊</strong></li>
<li>点击"user@example.com" → 自动识别邮箱并模糊</li>
<li>点击"sk-1234..." → 自动识别密钥并模糊</li>
<li>点击"手机号:"文字 → 模糊整行</li>
</ul>
</div>
<div class="test-box">
<h3>测试 3复杂场景</h3>
<div class="example">
<p>用户 张三 的手机号是 186-0000-1234邮箱是 zhangsan@company.com</p>
</div>
<p>💡 <strong>测试方法:</strong></p>
<ul>
<li>点击"张三" → 只模糊名字</li>
<li>点击"186-0000-1234" → 只模糊手机号</li>
<li>点击"zhangsan@company.com" → 只模糊邮箱</li>
<li>点击"用户"或"的手机号是" → 模糊整行</li>
</ul>
</div>
<div class="test-box">
<h3>测试 4列表中的纯文本</h3>
<div class="example">
<ul>
<li>订单号 ORD-20250101-001</li>
<li>金额 ¥1,288.00</li>
<li>地址 北京市朝阳区某某街道123号</li>
</ul>
</div>
<p>💡 <strong>测试方法:</strong></p>
<ul>
<li>点击订单号 → 自动识别并模糊</li>
<li>点击金额 → 模糊数字</li>
<li>点击地址 → 模糊地址</li>
</ul>
</div>
<div class="info">
<strong>🔍 工作原理:</strong><br><br>
<strong>1. 检测文本节点</strong><br>
当你点击纯文本时,使用 <code>document.caretRangeFromPoint()</code> 检测是否点在文本节点上<br><br>
<strong>2. 智能识别模式</strong><br>
扫描文本内容,识别常见的敏感信息模式:<br>
• 手机号:<code>/\d{3,}[-\d]*/</code><br>
• 邮箱:<code>/[\w\.-]+@[\w\.-]+\.\w+/</code><br>
• Token/密钥:<code>/[a-zA-Z0-9_-]{10,}/</code><br><br>
<strong>3. 自动包裹</strong><br>
如果识别到敏感信息,自动创建 <code>&lt;span&gt;</code> 包裹该部分<br>
如果没识别到特定模式,包裹整个文本节点<br><br>
<strong>4. 应用模糊</strong><br>
对新创建的 <code>&lt;span&gt;</code> 应用模糊效果
</div>
<div class="success">
<strong>✅ 预期效果:</strong><br>
• 点击敏感信息(手机号、邮箱等)→ 只模糊该信息<br>
• 点击普通文字 → 模糊整个文本节点或整行<br>
• 支持在同一行中选择性模糊不同部分<br>
• 不破坏原始 HTML 结构(只在需要时添加 span
</div>
<div class="info">
<strong>💡 Console 日志:</strong><br>
按 F12 打开控制台,点击文字时会看到:<br>
<code>[BlurText] Detected text node: ...</code><br>
<code>[BlurText] Found sensitive value: ...</code><br>
<code>[BlurText] Wrapped text node in span</code>
</div>
</div>
</body>
</html>

228
test.html Normal file
View File

@@ -0,0 +1,228 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlurText 测试页面</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
padding: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 16px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
h1 {
color: #667eea;
margin-bottom: 10px;
}
.subtitle {
color: #888;
margin-bottom: 30px;
font-size: 14px;
}
.test-section {
margin-bottom: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #667eea;
}
.test-section h2 {
color: #333;
font-size: 18px;
margin-bottom: 15px;
}
.test-content {
line-height: 1.8;
color: #555;
}
.sensitive-info {
background: #fff3cd;
padding: 10px 15px;
border-radius: 6px;
margin: 10px 0;
border-left: 3px solid #ffc107;
}
.info-box {
background: #e3f2fd;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
border-left: 4px solid #2196f3;
}
.info-box strong {
color: #1976d2;
}
code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
color: #e91e63;
}
.card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin: 15px 0;
}
.label {
display: inline-block;
background: #667eea;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
margin-bottom: 8px;
}
ul {
margin-left: 20px;
margin-top: 10px;
}
li {
margin: 8px 0;
color: #555;
}
</style>
</head>
<body>
<div class="container">
<h1>🔒 BlurText 测试页面</h1>
<p class="subtitle">使用这个页面测试模糊功能是否正常工作</p>
<div class="info-box">
<strong>📖 测试步骤:</strong>
<ol style="margin-left: 20px; margin-top: 10px;">
<li>点击浏览器工具栏的 <strong>BlurText</strong> 图标</li>
<li>点击弹出窗口中的 <strong>"开启模糊模式"</strong> 按钮</li>
<li>页面顶部应该显示提示:"模糊模式已开启"</li>
<li>鼠标悬停在下方文字上,应该有蓝色虚线框高亮</li>
<li>点击任意文字,应该变模糊</li>
<li>再次点击模糊的文字,应该取消模糊</li>
<li><code>ESC</code> 键退出模糊模式</li>
</ol>
</div>
<div class="test-section">
<h2>📝 测试区域 1普通文本</h2>
<div class="test-content">
<p>这是一段普通的文本内容,你可以点击这段文字来测试模糊功能。</p>
<p>模糊功能应该可以立即生效,不需要屏幕录制或分享。</p>
</div>
</div>
<div class="test-section">
<h2>🔐 测试区域 2敏感信息模拟</h2>
<div class="test-content">
<div class="sensitive-info">
<strong>API Key:</strong> <span>sk-1234567890abcdefghijklmnopqrstuvwxyz</span>
</div>
<div class="sensitive-info">
<strong>手机号:</strong> <span>138-0000-1234</span>
</div>
<div class="sensitive-info">
<strong>邮箱:</strong> <span>example@company.com</span>
</div>
<div class="sensitive-info">
<strong>密码:</strong> <span>MyS3cur3P@ssw0rd!</span>
</div>
</div>
</div>
<div class="test-section">
<h2>📊 测试区域 3卡片内容</h2>
<div class="card">
<span class="label">用户信息</span>
<p><strong>姓名:</strong><span>张三</span></p>
<p><strong>身份证:</strong><span>110101199001011234</span></p>
<p><strong>银行卡:</strong><span>6222 0000 1111 2222</span></p>
</div>
<div class="card">
<span class="label">订单信息</span>
<p><strong>订单号:</strong><span>ORD-2025-001234</span></p>
<p><strong>金额:</strong><span>¥1,288.00</span></p>
<p><strong>地址:</strong><span>北京市朝阳区某某街道123号</span></p>
</div>
</div>
<div class="test-section">
<h2>📋 测试区域 4列表内容</h2>
<div class="test-content">
<ul>
<li>项目 A - 配置文件路径:/etc/config/secret.yml</li>
<li>项目 B - 数据库连接mysql://user:password@localhost:3306</li>
<li>项目 C - 访问令牌eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</li>
<li>项目 D - 私钥文件:~/.ssh/id_rsa</li>
</ul>
</div>
</div>
<div class="info-box" style="background: #fff3e0; border-left-color: #ff9800;">
<strong>⚠️ 故障排查:</strong>
<p style="margin-top: 10px;">如果功能不工作,请检查:</p>
<ul style="margin-left: 20px; margin-top: 8px;">
<li>扩展是否正确安装(查看 <code>chrome://extensions/</code></li>
<li>扩展是否启用(开关是否打开)</li>
<li>是否显示任何错误(右键扩展图标 → 检查弹出窗口)</li>
<li>刷新页面后重试</li>
<li>打开浏览器控制台F12查看是否有错误信息</li>
</ul>
</div>
<div class="info-box" style="background: #e8f5e9; border-left-color: #4caf50; margin-top: 30px;">
<strong>✅ 预期行为:</strong>
<ul style="margin-left: 20px; margin-top: 8px;">
<li>开启模糊模式后,页面顶部显示紫色提示条</li>
<li>鼠标移动到文字上,出现蓝色虚线边框高亮</li>
<li>点击文字后,该文字变模糊(毛玻璃效果)</li>
<li>鼠标悬停在已模糊的文字上,显示"点击取消模糊"提示</li>
<li>再次点击已模糊文字,模糊效果消失</li>
</ul>
</div>
</div>
<script>
// 检测扩展是否正确加载
console.log('测试页面已加载');
console.log('检查 content script 是否注入...');
setTimeout(() => {
if (document.body.classList.contains('blurtext-mode')) {
console.log('✅ 模糊模式已启用');
} else {
console.log(' 模糊模式未启用(这是正常的,需要手动开启)');
}
}, 1000);
</script>
</body>
</html>

279
usage-guide.html Normal file
View File

@@ -0,0 +1,279 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlurText 使用说明</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
max-width: 900px;
margin: 40px auto;
padding: 20px;
line-height: 1.6;
background: #f5f5f5;
}
.container {
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
h1 {
color: #667eea;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
}
h2 {
color: #764ba2;
margin-top: 30px;
}
.example {
background: #f8f9fa;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #667eea;
border-radius: 4px;
}
.example-title {
font-weight: bold;
color: #667eea;
margin-bottom: 10px;
}
.demo-row {
display: flex;
gap: 20px;
margin: 15px 0;
align-items: center;
}
.demo-box {
flex: 1;
padding: 15px;
border: 2px solid #ddd;
border-radius: 8px;
background: white;
}
.good {
border-color: #4caf50;
background: #e8f5e9;
}
.bad {
border-color: #f44336;
background: #ffebee;
}
code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
color: #e91e63;
font-size: 14px;
}
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
}
.badge {
display: inline-block;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
margin-left: 10px;
}
.badge-success {
background: #4caf50;
color: white;
}
.badge-error {
background: #f44336;
color: white;
}
.note {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
.tip {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>🔒 BlurText 使用说明</h1>
<h2>⚠️ 重要限制</h2>
<div class="note">
<strong>注意:</strong>BlurText 只能模糊 <strong>HTML 元素</strong>,不能直接模糊纯文本节点。<br>
如果你想要单独模糊某个值(如手机号、姓名),该值必须包裹在 HTML 标签中(如 <code>&lt;span&gt;</code>)。
</div>
<h2>✅ 正确的 HTML 结构</h2>
<div class="example">
<div class="example-title">示例 1可以单独模糊值</div>
<div class="demo-box good">
<p><strong>姓名:</strong><span>张三</span></p>
</div>
<pre><code>&lt;p&gt;
&lt;strong&gt;姓名:&lt;/strong&gt;
&lt;span&gt;张三&lt;/span&gt;
&lt;/p&gt;</code></pre>
<p><strong>效果:</strong></p>
<ul>
<li>鼠标移到"姓名:"→ 高亮 <code>&lt;strong&gt;</code> 标签</li>
<li>鼠标移到"张三" → 高亮 <code>&lt;span&gt;</code> 标签</li>
<li>点击"姓名:"→ 模糊标签</li>
<li>点击"张三" → <strong>只模糊"张三"</strong> <span class="badge badge-success">推荐</span></li>
<li>点击整行空白处 → 模糊整个 <code>&lt;p&gt;</code></li>
</ul>
</div>
<div class="example">
<div class="example-title">示例 2复杂结构</div>
<div class="demo-box good">
<div style="background: #fff3cd; padding: 10px; border-radius: 4px;">
<strong>手机号:</strong><span>138-0000-1234</span>
</div>
</div>
<pre><code>&lt;div class="sensitive-info"&gt;
&lt;strong&gt;手机号:&lt;/strong&gt;
&lt;span&gt;138-0000-1234&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p><strong>效果:</strong></p>
<ul>
<li>点击"手机号:"→ 模糊标签</li>
<li>点击"138-0000-1234" → 只模糊数字</li>
<li>点击背景区域 → 模糊整个 div</li>
</ul>
</div>
<h2>❌ 错误的 HTML 结构</h2>
<div class="example">
<div class="example-title">示例 3无法单独模糊值</div>
<div class="demo-box bad">
<p><strong>姓名:</strong>张三</p>
</div>
<pre><code>&lt;p&gt;
&lt;strong&gt;姓名:&lt;/strong&gt;
张三
&lt;/p&gt;</code></pre>
<p><strong>问题:</strong></p>
<ul>
<li>"张三"是<strong>纯文本节点</strong>,不在任何标签里</li>
<li>鼠标移到"张三"上,实际高亮的是整个 <code>&lt;p&gt;</code></li>
<li>点击"张三" → 模糊整个 <code>&lt;p&gt;</code>(包括"姓名:"</li>
<li><strong>无法单独模糊"张三"</strong> <span class="badge badge-error">不推荐</span></li>
</ul>
</div>
<div class="tip">
<strong>💡 解决方案:</strong><br>
如果你在自己的网页上使用 BlurText请确保需要单独模糊的内容包裹在标签中
<pre><code>// 错误 ❌
&lt;p&gt;手机号138-0000-1234&lt;/p&gt;
// 正确 ✅
&lt;p&gt;手机号:&lt;span&gt;138-0000-1234&lt;/span&gt;&lt;/p&gt;</code></pre>
</div>
<h2>🎯 最佳实践</h2>
<div class="example">
<div class="example-title">推荐的 HTML 结构模板</div>
<pre><code>&lt;!-- 表单字段 --&gt;
&lt;div class="field"&gt;
&lt;label&gt;姓名&lt;/label&gt;
&lt;span class="value"&gt;张三&lt;/span&gt;
&lt;/div&gt;
&lt;!-- 列表项 --&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Key:&lt;/strong&gt;
&lt;code&gt;sk-1234567890abcdef&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- 表格 --&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;手机号&lt;/td&gt;
&lt;td&gt;&lt;span&gt;138-0000-1234&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</code></pre>
</div>
<h2>🔧 实际使用技巧</h2>
<ol>
<li><strong>识别 HTML 结构</strong>
<ul>
<li>按 F12 打开开发者工具</li>
<li>使用元素选择器(左上角箭头图标)</li>
<li>点击网页上的内容,查看 HTML 结构</li>
<li>如果值在独立标签里,就可以单独模糊</li>
</ul>
</li>
<li><strong>灵活使用模糊范围</strong>
<ul>
<li>如果无法单独模糊值,模糊整行或整个区块也能达到目的</li>
<li>使用鼠标悬停预览,看蓝色边框高亮的是哪个元素</li>
<li>根据高亮范围决定是否点击</li>
</ul>
</li>
<li><strong>对于第三方网站</strong>
<ul>
<li>大部分网站的表单、表格、列表都有良好的结构</li>
<li>现代网站通常会把数据包裹在 <code>&lt;span&gt;</code> 或其他标签中</li>
<li>如果遇到无法单独模糊的情况,模糊整行即可</li>
</ul>
</li>
</ol>
<div class="note">
<strong>📌 总结:</strong><br>
BlurText 的粒度取决于网页的 HTML 结构。结构越清晰(标签层级越细),模糊控制就越精确。这是浏览器扩展的技术限制,无法绕过。
</div>
<h2>🧪 测试建议</h2>
<p>打开 <a href="test.html">test.html</a> 测试页面,我已经更新了 HTML 结构,现在可以:</p>
<ul>
<li>✅ 单独模糊"138-0000-1234"</li>
<li>✅ 单独模糊"张三"</li>
<li>✅ 单独模糊各种敏感信息</li>
<li>✅ 或者模糊整行、整个区块</li>
</ul>
</div>
</body>
</html>