first commit
This commit is contained in:
7
.claude/settings.local.json
Normal file
7
.claude/settings.local.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(wc:*)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
227
CHANGELOG.md
Normal file
227
CHANGELOG.md
Normal 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
204
FIXES.md
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
# BlurText 修复说明
|
||||||
|
|
||||||
|
## 🔄 重大变更(2025-01-10)
|
||||||
|
|
||||||
|
### 移除文本节点模糊功能
|
||||||
|
|
||||||
|
**决策原因**:
|
||||||
|
文本节点自动检测和包裹功能虽然看起来智能,但引入了过多复杂性和bug:
|
||||||
|
1. 预览高亮和实际模糊的交互逻辑复杂
|
||||||
|
2. DOM 操作频繁,容易出现元素消失的问题
|
||||||
|
3. 预览 span 的生命周期管理困难
|
||||||
|
4. 用户体验不够稳定
|
||||||
|
|
||||||
|
**回归简单模式**:
|
||||||
|
- ✅ 只模糊 HTML 元素(span, div, p, td 等)
|
||||||
|
- ✅ 鼠标悬停高亮,点击即模糊
|
||||||
|
- ✅ 稳定可靠,不会出现元素消失的问题
|
||||||
|
- ❌ 不再自动检测和包裹纯文本节点
|
||||||
|
- ❌ 不再智能识别敏感信息(手机号、邮箱等)
|
||||||
|
|
||||||
|
**使用建议**:
|
||||||
|
- 如果需要模糊纯文本,可以先手动用 `<span>` 包裹
|
||||||
|
- 或者点击包含该文本的父元素进行整体模糊
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 已修复的问题
|
||||||
|
|
||||||
|
### 问题 1:Popup 卡片在点击页面后消失
|
||||||
|
|
||||||
|
**症状**:
|
||||||
|
- 开启模糊模式后,点击扩展图标打开 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
186
INSTALL.md
Normal 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
111
README.md
Normal 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
113
content.css
Normal 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
372
content.js
Normal 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
173
icon-generator.html
Normal 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
BIN
icons/icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
icons/icon16.png
Normal file
BIN
icons/icon16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 695 B |
BIN
icons/icon32.png
Normal file
BIN
icons/icon32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
icons/icon48.png
Normal file
BIN
icons/icon48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
33
manifest.json
Normal file
33
manifest.json
Normal 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
282
popup.html
Normal 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
168
popup.js
Normal 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
205
test-textnode.html
Normal 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><span></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><span></code> 包裹该部分<br>
|
||||||
|
如果没识别到特定模式,包裹整个文本节点<br><br>
|
||||||
|
|
||||||
|
<strong>4. 应用模糊</strong><br>
|
||||||
|
对新创建的 <code><span></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
228
test.html
Normal 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
279
usage-guide.html
Normal 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><span></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><p>
|
||||||
|
<strong>姓名:</strong>
|
||||||
|
<span>张三</span>
|
||||||
|
</p></code></pre>
|
||||||
|
<p>✅ <strong>效果:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>鼠标移到"姓名:"→ 高亮 <code><strong></code> 标签</li>
|
||||||
|
<li>鼠标移到"张三" → 高亮 <code><span></code> 标签</li>
|
||||||
|
<li>点击"姓名:"→ 模糊标签</li>
|
||||||
|
<li>点击"张三" → <strong>只模糊"张三"</strong> <span class="badge badge-success">推荐</span></li>
|
||||||
|
<li>点击整行空白处 → 模糊整个 <code><p></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><div class="sensitive-info">
|
||||||
|
<strong>手机号:</strong>
|
||||||
|
<span>138-0000-1234</span>
|
||||||
|
</div></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><p>
|
||||||
|
<strong>姓名:</strong>
|
||||||
|
张三
|
||||||
|
</p></code></pre>
|
||||||
|
<p>❌ <strong>问题:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>"张三"是<strong>纯文本节点</strong>,不在任何标签里</li>
|
||||||
|
<li>鼠标移到"张三"上,实际高亮的是整个 <code><p></code></li>
|
||||||
|
<li>点击"张三" → 模糊整个 <code><p></code>(包括"姓名:")</li>
|
||||||
|
<li><strong>无法单独模糊"张三"</strong> <span class="badge badge-error">不推荐</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tip">
|
||||||
|
<strong>💡 解决方案:</strong><br>
|
||||||
|
如果你在自己的网页上使用 BlurText,请确保需要单独模糊的内容包裹在标签中:
|
||||||
|
<pre><code>// 错误 ❌
|
||||||
|
<p>手机号:138-0000-1234</p>
|
||||||
|
|
||||||
|
// 正确 ✅
|
||||||
|
<p>手机号:<span>138-0000-1234</span></p></code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>🎯 最佳实践</h2>
|
||||||
|
|
||||||
|
<div class="example">
|
||||||
|
<div class="example-title">推荐的 HTML 结构模板</div>
|
||||||
|
<pre><code><!-- 表单字段 -->
|
||||||
|
<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></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><span></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>
|
||||||
Reference in New Issue
Block a user