镜像自地址
https://github.com/lhly/search-syntax-pro.git
已同步 2026-10-20 14:27:47 +08:00
实现搜索引擎页面的悬浮按钮功能,支持拖动调整位置和点击打开高级搜索面板。新增浮动面板组件,包含完整的搜索表单和交互逻辑。优化URL解码和搜索引擎选择器配置,提升用户体验。 - 新增浮动面板组件及样式 - 实现悬浮按钮的拖动和点击交互 - 添加URL安全解码工具函数 - 配置搜索引擎选择器和注入位置 - 优化多语言支持和响应式布局 - 添加性能测试和单元测试
756 行
18 KiB
Markdown
756 行
18 KiB
Markdown
# 需求确认文档 - 改进型悬浮面板交互
|
||
|
||
## 📋 基本信息
|
||
|
||
- **功能名称**: 改进型悬浮面板交互 (Improved Floating Panel)
|
||
- **功能代号**: `improved-floating-panel`
|
||
- **目标版本**: v1.7.3+
|
||
- **优先级**: 高
|
||
- **创建时间**: 2025-11-20
|
||
- **需求状态**: ✅ 已确认 (质量评分: 94/100)
|
||
|
||
---
|
||
|
||
## 🎯 原始需求
|
||
|
||
**用户请求**: "请你实现方案 2"
|
||
|
||
**方案2定义** (来自之前对话):
|
||
> 改进型悬浮面板 - 优化现有的悬浮按钮功能,启用并改进 `FEATURE_FLAGS.enableFloatingButton`,实现类似 Grammarly 的优雅交互体验。
|
||
|
||
---
|
||
|
||
## 🏗️ 仓库上下文分析
|
||
|
||
### 现有基础设施
|
||
|
||
**项目类型**: Chrome Extension (Manifest V3)
|
||
**技术栈**: React 18 + TypeScript 5 + Vite 5
|
||
**当前版本**: v1.7.2
|
||
|
||
### 相关现有代码
|
||
|
||
1. **Content Script**: `/src/content/index.ts`
|
||
- 已有 `injectSearchFeatures()` 函数框架
|
||
- 功能开关: `FEATURE_FLAGS.enableFloatingButton = false` (当前禁用)
|
||
- 已有搜索引擎检测逻辑: `isSearchEnginePage()`
|
||
|
||
2. **Popup 组件**: `/src/popup/App.tsx`
|
||
- 完整的语法构建器UI
|
||
- React 组件化架构
|
||
- 支持 SearchForm、HistoryManager 等功能
|
||
|
||
3. **消息通信**: `/src/background/index.ts`
|
||
- 已有 `chrome.runtime.onMessage` 监听器
|
||
- 支持 `open_popup`、`quick_search` 等消息类型
|
||
|
||
4. **适配器系统**: `/src/services/adapters/`
|
||
- 已支持 Baidu、Google、Bing 等10个搜索引擎
|
||
- 适配器工厂模式: `AdapterFactory.createAdapter(url)`
|
||
|
||
### 集成约束
|
||
|
||
- **Chrome 版本**: Minimum Chrome 91 (manifest.json 定义)
|
||
- **CSP 策略**: `script-src 'self'; object-src 'self'`
|
||
- **国际化**: 必须支持 zh-CN 和 en-US
|
||
- **主题**: 必须支持 light/dark 模式
|
||
|
||
---
|
||
|
||
## ✅ 需求澄清过程
|
||
|
||
### 第一轮澄清 (2025-11-20)
|
||
|
||
#### 问题组1: 交互体验细节
|
||
|
||
**Q1.1: 内联图标的显示位置?**
|
||
- ✅ 用户选择: **C - 搜索框下方**
|
||
- 决策说明: 作为提示/建议出现,不侵入搜索框本身
|
||
|
||
**Q1.2: 图标触发方式?**
|
||
- ✅ 用户选择: **B - 焦点+悬停触发**
|
||
- 决策说明: 焦点时显示,鼠标悬停在搜索区域也显示
|
||
|
||
**Q1.3: 模态面板的尺寸和位置?**
|
||
- ✅ 用户选择: **B - 大面板 (800px × 600px)**
|
||
- 决策说明: 提供充足空间展示完整语法构建器
|
||
|
||
**Q1.4: 点击遮罩层的行为?**
|
||
- ✅ 用户选择: **A - 关闭面板**
|
||
- 决策说明: 点击背景即关闭,符合标准模态框交互
|
||
|
||
#### 问题组2: 技术实现选择
|
||
|
||
**Q2.1: 面板渲染方式?**
|
||
- ✅ 用户选择: **A - Shadow DOM + iframe**
|
||
- 决策说明: 完全隔离样式,避免与搜索引擎页面冲突
|
||
|
||
**Q2.2: 如何复用现有 popup 组件?**
|
||
- ✅ 用户选择: **C - 消息传递方式**
|
||
- 决策说明: content script 负责UI框架,业务逻辑通过消息传递
|
||
|
||
**Q2.3: 搜索引擎 DOM 适配策略?**
|
||
- ✅ 用户选择: **A - 配置化适配**
|
||
- 决策说明: 为每个搜索引擎定义选择器配置
|
||
|
||
#### 问题组3: 边缘情况处理
|
||
|
||
**Q3.1: DOM 结构更新处理?**
|
||
- ✅ 用户选择: **A - 自动重试**
|
||
- 决策说明: 监听 DOM 变化,自动重新注入
|
||
|
||
**Q3.2: 面板打开时焦点管理?**
|
||
- ✅ 用户选择: **A - 保持显示**
|
||
- 决策说明: 面板打开后不受搜索框焦点影响
|
||
|
||
**Q3.3: 快捷键冲突处理?**
|
||
- ✅ 用户选择: **C - 禁用快捷键**
|
||
- 决策说明: 面板只能通过图标触发,避免与现有快捷键冲突
|
||
|
||
**Q3.4: 多标签页处理?**
|
||
- ✅ 用户选择: **A - 独立状态**
|
||
- 决策说明: 每个标签页的面板状态独立
|
||
|
||
#### 问题组4: 功能范围确认
|
||
|
||
**Q4.1: 功能开关策略?**
|
||
- ✅ 用户选择: **A - 默认启用**
|
||
- 决策说明: 将 `FEATURE_FLAGS.enableFloatingButton` 改为 `true`
|
||
|
||
**Q4.2: 与现有右键菜单的关系?**
|
||
- ✅ 用户选择: **A - 并存**
|
||
- 决策说明: 两种入口都保留,用户自由选择
|
||
|
||
**Q4.3: 支持的浏览器版本?**
|
||
- ✅ 用户选择: **A - Chrome 91+**
|
||
- 决策说明: 与 manifest.json 保持一致
|
||
|
||
---
|
||
|
||
## 📐 详细功能规格
|
||
|
||
### 1. 用户交互流程
|
||
|
||
```
|
||
用户访问搜索引擎页面
|
||
↓
|
||
页面加载完成,content script 注入
|
||
↓
|
||
检测搜索框元素
|
||
↓
|
||
在搜索框下方注入触发图标 (初始隐藏)
|
||
↓
|
||
[触发条件1] 用户点击搜索框 (焦点事件)
|
||
[触发条件2] 鼠标悬停在搜索区域 (hover事件)
|
||
↓
|
||
图标从下方滑入/淡入显示
|
||
↓
|
||
用户点击图标
|
||
↓
|
||
显示模态遮罩层 (半透明黑色背景)
|
||
↓
|
||
在遮罩层上显示居中的面板 (800×600px)
|
||
↓
|
||
面板内通过 iframe 加载 popup/index.html
|
||
↓
|
||
用户在面板中构建搜索语法
|
||
↓
|
||
点击"应用"或"搜索"按钮
|
||
↓
|
||
通过消息传递将语法发送到 content script
|
||
↓
|
||
content script 将语法填充到搜索框
|
||
↓
|
||
关闭面板和遮罩层
|
||
↓
|
||
(可选) 自动触发搜索
|
||
```
|
||
|
||
### 2. 技术架构设计
|
||
|
||
#### 2.1 组件结构
|
||
|
||
```
|
||
Content Script (src/content/index.ts)
|
||
├── FloatingPanelManager (新增)
|
||
│ ├── createTriggerIcon() // 创建触发图标
|
||
│ ├── createModalOverlay() // 创建遮罩层
|
||
│ ├── createPanelIframe() // 创建面板 iframe
|
||
│ ├── handleShow/Hide() // 显示/隐藏逻辑
|
||
│ └── handleMessage() // 消息通信
|
||
├── SearchEngineAdapter (扩展)
|
||
│ ├── getSearchInputSelector() // 获取搜索框选择器
|
||
│ ├── getInsertPosition() // 获取图标插入位置
|
||
│ └── fillSearchInput(query) // 填充搜索框
|
||
└── DOMObserver (新增)
|
||
├── observeSearchBox() // 监听搜索框变化
|
||
└── reInjectOnChange() // DOM变化时重新注入
|
||
```
|
||
|
||
#### 2.2 搜索引擎适配配置
|
||
|
||
```typescript
|
||
// src/config/search-engine-selectors.ts (新建)
|
||
interface SearchEngineConfig {
|
||
searchInputSelector: string;
|
||
searchContainerSelector: string;
|
||
iconInsertPosition: 'afterend' | 'beforeend';
|
||
iconOffsetY: string; // CSS position offset
|
||
}
|
||
|
||
const ENGINE_CONFIGS: Record<string, SearchEngineConfig> = {
|
||
baidu: {
|
||
searchInputSelector: 'input#kw',
|
||
searchContainerSelector: '#form',
|
||
iconInsertPosition: 'afterend',
|
||
iconOffsetY: '8px'
|
||
},
|
||
google: {
|
||
searchInputSelector: 'input[name="q"]',
|
||
searchContainerSelector: 'div.RNNXgb',
|
||
iconInsertPosition: 'afterend',
|
||
iconOffsetY: '8px'
|
||
},
|
||
bing: {
|
||
searchInputSelector: 'input#sb_form_q',
|
||
searchContainerSelector: 'form#sb_form',
|
||
iconInsertPosition: 'afterend',
|
||
iconOffsetY: '8px'
|
||
}
|
||
};
|
||
```
|
||
|
||
#### 2.3 消息通信协议
|
||
|
||
```typescript
|
||
// Content Script → Background → Popup
|
||
interface MessageProtocol {
|
||
// 请求打开面板 (content → background)
|
||
type: 'open_floating_panel';
|
||
|
||
// 应用搜索语法 (popup → content)
|
||
type: 'apply_search_syntax';
|
||
payload: {
|
||
query: string;
|
||
autoSearch: boolean;
|
||
};
|
||
|
||
// 关闭面板 (popup → content)
|
||
type: 'close_floating_panel';
|
||
}
|
||
```
|
||
|
||
### 3. UI/UX 规格
|
||
|
||
#### 3.1 触发图标
|
||
|
||
**位置**: 搜索框下方,水平居中
|
||
**尺寸**: 120px × 32px
|
||
**样式**:
|
||
- 背景: `bg-blue-500` (主题色)
|
||
- 文字: "⚙️ 高级语法" (中文) / "⚙️ Advanced" (英文)
|
||
- 边框圆角: `rounded-md`
|
||
- 阴影: `shadow-md`
|
||
|
||
**动画**:
|
||
- 显示: 从下方淡入 + 向上滑动 (`fadeInUp`, 200ms)
|
||
- 隐藏: 向下淡出 (`fadeOutDown`, 200ms)
|
||
|
||
**交互状态**:
|
||
- Hover: 放大 1.05 倍 + 阴影加深
|
||
- Active: 按下效果 (scale 0.98)
|
||
|
||
#### 3.2 模态遮罩层
|
||
|
||
**样式**:
|
||
- 背景: `rgba(0, 0, 0, 0.5)` (半透明黑色)
|
||
- z-index: `9999`
|
||
- 全屏覆盖: `fixed top-0 left-0 w-full h-full`
|
||
|
||
**动画**:
|
||
- 显示: 淡入 (`fadeIn`, 200ms)
|
||
- 隐藏: 淡出 (`fadeOut`, 200ms)
|
||
|
||
**交互**:
|
||
- 点击遮罩 → 关闭面板
|
||
|
||
#### 3.3 面板容器
|
||
|
||
**尺寸**: 800px × 600px
|
||
**位置**: 屏幕居中
|
||
**样式**:
|
||
- 背景: 白色 (light) / 深灰 (dark)
|
||
- 边框圆角: `rounded-lg`
|
||
- 阴影: `shadow-2xl`
|
||
|
||
**动画**:
|
||
- 显示: 缩放弹出 (`zoomIn`, 300ms, ease-out`)
|
||
- 隐藏: 缩放消失 (`zoomOut`, 200ms, ease-in`)
|
||
|
||
**内容**:
|
||
- iframe 加载 `/src/popup/index.html`
|
||
- iframe 尺寸: 100% × 100%
|
||
- 无边框: `border: none`
|
||
|
||
#### 3.4 响应式适配
|
||
|
||
```css
|
||
/* 小屏幕适配 (< 900px) */
|
||
@media (max-width: 900px) {
|
||
.ssp-panel {
|
||
width: 90vw;
|
||
height: 80vh;
|
||
max-width: 700px;
|
||
max-height: 600px;
|
||
}
|
||
}
|
||
|
||
/* 极小屏幕 (< 600px) */
|
||
@media (max-width: 600px) {
|
||
.ssp-panel {
|
||
width: 95vw;
|
||
height: 90vh;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 功能需求详细说明
|
||
|
||
#### 4.1 自动 DOM 注入
|
||
|
||
**触发时机**:
|
||
- 页面加载完成 (`DOMContentLoaded`)
|
||
- 页面 URL 变化 (SPA 路由切换)
|
||
- DOM 结构变化 (MutationObserver)
|
||
|
||
**注入逻辑**:
|
||
1. 检测当前页面是否为搜索引擎
|
||
2. 使用配置化选择器定位搜索框
|
||
3. 在搜索框下方插入触发图标
|
||
4. 绑定焦点/悬停事件监听器
|
||
|
||
**容错处理**:
|
||
- 如果选择器未找到元素 → 延迟500ms重试 (最多3次)
|
||
- 如果搜索引擎未配置 → 静默失败,不注入
|
||
- 如果已注入 → 跳过,避免重复
|
||
|
||
#### 4.2 焦点与悬停管理
|
||
|
||
**焦点事件**:
|
||
```typescript
|
||
searchInput.addEventListener('focus', () => {
|
||
showTriggerIcon();
|
||
});
|
||
|
||
searchInput.addEventListener('blur', () => {
|
||
// 延迟隐藏,防止点击图标时过早消失
|
||
setTimeout(() => {
|
||
if (!isIconHovered && !isPanelOpen) {
|
||
hideTriggerIcon();
|
||
}
|
||
}, 200);
|
||
});
|
||
```
|
||
|
||
**悬停事件**:
|
||
```typescript
|
||
searchContainer.addEventListener('mouseenter', () => {
|
||
showTriggerIcon();
|
||
});
|
||
|
||
searchContainer.addEventListener('mouseleave', () => {
|
||
if (!searchInput.matches(':focus') && !isPanelOpen) {
|
||
hideTriggerIcon();
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 4.3 面板生命周期
|
||
|
||
**打开流程**:
|
||
1. 创建遮罩层元素 (如不存在)
|
||
2. 创建面板容器 (如不存在)
|
||
3. 创建 iframe 并加载 popup HTML
|
||
4. 添加到 Shadow DOM 中
|
||
5. 执行显示动画
|
||
6. 设置 `isPanelOpen = true`
|
||
|
||
**关闭流程**:
|
||
1. 执行隐藏动画
|
||
2. 动画结束后移除遮罩层和面板
|
||
3. 清理事件监听器
|
||
4. 设置 `isPanelOpen = false`
|
||
|
||
**消息通信**:
|
||
- iframe 加载完成 → 发送初始化消息
|
||
- 用户点击"应用" → popup 发送 `apply_search_syntax` 消息
|
||
- content script 接收 → 填充搜索框 → 关闭面板
|
||
|
||
#### 4.4 搜索框填充
|
||
|
||
**填充逻辑**:
|
||
```typescript
|
||
function fillSearchInput(query: string, autoSearch: boolean = false) {
|
||
const searchInput = getSearchInput();
|
||
|
||
// 设置值
|
||
searchInput.value = query;
|
||
|
||
// 触发 input 事件 (某些搜索引擎需要)
|
||
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||
|
||
// 触发 change 事件
|
||
searchInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||
|
||
// 设置焦点
|
||
searchInput.focus();
|
||
|
||
// 可选: 自动提交搜索
|
||
if (autoSearch) {
|
||
const form = searchInput.closest('form');
|
||
if (form) {
|
||
form.submit();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4.5 多标签页独立状态
|
||
|
||
**实现方式**:
|
||
- 每个标签页的 content script 独立运行
|
||
- 不使用 `chrome.storage` 共享面板状态
|
||
- 每个标签页的面板 DOM 元素独立创建/销毁
|
||
|
||
#### 4.6 国际化支持
|
||
|
||
**触发图标文本**:
|
||
```typescript
|
||
const iconText = getCurrentLanguage() === 'zh-CN'
|
||
? '⚙️ 高级语法'
|
||
: '⚙️ Advanced Syntax';
|
||
```
|
||
|
||
**iframe 页面语言**:
|
||
- iframe 加载的 popup 会自动读取 `chrome.storage` 中的语言设置
|
||
- 无需额外处理
|
||
|
||
#### 4.7 主题适配
|
||
|
||
**Shadow DOM 样式**:
|
||
```css
|
||
/* 遮罩层根据系统主题自动适配 */
|
||
.ssp-modal-overlay {
|
||
background: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
/* 面板容器根据主题切换 */
|
||
.ssp-panel-container {
|
||
background: var(--bg-color);
|
||
color: var(--text-color);
|
||
}
|
||
|
||
/* 从 chrome.storage 读取主题设置并注入 CSS 变量 */
|
||
```
|
||
|
||
### 5. 边缘情况处理
|
||
|
||
#### 5.1 DOM 结构变化
|
||
|
||
**问题**: 搜索引擎页面 DOM 更新导致图标消失
|
||
|
||
**解决方案**:
|
||
```typescript
|
||
const observer = new MutationObserver((mutations) => {
|
||
const iconExists = document.querySelector('#ssp-trigger-icon');
|
||
if (!iconExists && isSearchEnginePage()) {
|
||
console.log('[SSP] DOM changed, re-injecting icon');
|
||
setTimeout(() => injectTriggerIcon(), 500);
|
||
}
|
||
});
|
||
|
||
observer.observe(document.body, {
|
||
childList: true,
|
||
subtree: true
|
||
});
|
||
```
|
||
|
||
**限流优化**:
|
||
- 使用 debounce 避免频繁重注入
|
||
- 最多每500ms检查一次
|
||
|
||
#### 5.2 iframe 加载失败
|
||
|
||
**问题**: iframe 无法加载 popup HTML
|
||
|
||
**解决方案**:
|
||
```typescript
|
||
iframe.addEventListener('error', () => {
|
||
console.error('[SSP] Failed to load popup iframe');
|
||
showErrorMessage('面板加载失败,请刷新页面重试');
|
||
closePanelAfterDelay(3000);
|
||
});
|
||
|
||
iframe.addEventListener('load', () => {
|
||
console.log('[SSP] Popup iframe loaded successfully');
|
||
initMessageChannel();
|
||
});
|
||
```
|
||
|
||
#### 5.3 CSP 策略阻止
|
||
|
||
**问题**: 某些搜索引擎的 CSP 策略阻止 iframe
|
||
|
||
**解决方案**:
|
||
- 使用 `chrome.runtime.getURL()` 确保 iframe src 为扩展内部资源
|
||
- 在 manifest.json 的 `web_accessible_resources` 中声明 popup HTML
|
||
|
||
```json
|
||
{
|
||
"web_accessible_resources": [
|
||
{
|
||
"resources": ["src/popup/index.html", "icons/*"],
|
||
"matches": ["<all_urls>"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.4 快速点击防抖
|
||
|
||
**问题**: 用户快速连续点击图标导致多个面板
|
||
|
||
**解决方案**:
|
||
```typescript
|
||
let isOpening = false;
|
||
|
||
function openPanel() {
|
||
if (isOpening || isPanelOpen) {
|
||
return;
|
||
}
|
||
|
||
isOpening = true;
|
||
|
||
// 执行打开逻辑...
|
||
|
||
setTimeout(() => {
|
||
isOpening = false;
|
||
}, 500);
|
||
}
|
||
```
|
||
|
||
#### 5.5 焦点陷阱 (Focus Trap)
|
||
|
||
**问题**: 面板打开后,Tab 键可能跳出面板
|
||
|
||
**解决方案**:
|
||
```typescript
|
||
// 在 iframe 内部的 popup 组件中实现焦点陷阱
|
||
modalElement.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Tab') {
|
||
trapFocusInModal(e);
|
||
}
|
||
|
||
if (e.key === 'Escape') {
|
||
closePanel();
|
||
}
|
||
});
|
||
```
|
||
|
||
### 6. 性能优化
|
||
|
||
#### 6.1 懒加载 iframe
|
||
|
||
**策略**: 只有在用户点击图标时才创建 iframe
|
||
|
||
```typescript
|
||
let panelIframe: HTMLIFrameElement | null = null;
|
||
|
||
function openPanel() {
|
||
if (!panelIframe) {
|
||
panelIframe = createPanelIframe();
|
||
}
|
||
|
||
showModal();
|
||
}
|
||
```
|
||
|
||
#### 6.2 CSS 动画硬件加速
|
||
|
||
**优化**:
|
||
```css
|
||
.ssp-panel-container {
|
||
will-change: transform, opacity;
|
||
transform: translateZ(0); /* 触发 GPU 加速 */
|
||
}
|
||
```
|
||
|
||
#### 6.3 事件委托
|
||
|
||
**优化**: 遮罩层点击事件使用事件委托
|
||
|
||
```typescript
|
||
overlay.addEventListener('click', (e) => {
|
||
if (e.target === overlay) {
|
||
closePanel();
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 6.4 节流/防抖
|
||
|
||
**应用场景**:
|
||
- DOM MutationObserver → debounce 500ms
|
||
- 窗口 resize 事件 → throttle 200ms
|
||
- 鼠标 hover 事件 → debounce 100ms
|
||
|
||
---
|
||
|
||
## 🎯 需求质量评分 (最终)
|
||
|
||
基于详细的需求澄清和规格定义,重新评分:
|
||
|
||
| 评估维度 | 得分 | 说明 |
|
||
|---------|------|------|
|
||
| **功能清晰度** (30分) | 28/30 | ✅ 交互流程、UI规格、动画效果已明确 |
|
||
| **技术具体性** (25分) | 24/25 | ✅ Shadow DOM架构、消息协议、适配配置已定义 |
|
||
| **实现完整性** (25分) | 24/25 | ✅ 边缘情况、错误处理、性能优化已覆盖 |
|
||
| **业务上下文** (20分) | 18/20 | ✅ 用户价值清晰,与现有功能协调明确 |
|
||
|
||
**最终总分**: **94/100** ✅
|
||
|
||
**质量等级**: A级 (优秀) - 可以进入实现阶段
|
||
|
||
---
|
||
|
||
## 📦 需求范围总结
|
||
|
||
### 包含功能 (In Scope)
|
||
|
||
✅ 在搜索框下方注入触发图标
|
||
✅ 焦点+悬停双触发机制
|
||
✅ 居中模态面板 (800×600px)
|
||
✅ Shadow DOM + iframe 渲染隔离
|
||
✅ 消息传递复用 popup 组件
|
||
✅ 配置化搜索引擎适配
|
||
✅ 自动 DOM 变化重注入
|
||
✅ 多标签页独立状态
|
||
✅ 点击遮罩关闭面板
|
||
✅ 国际化和主题支持
|
||
✅ 默认启用功能开关
|
||
✅ 与右键菜单并存
|
||
✅ Chrome 91+ 兼容性
|
||
|
||
### 不包含功能 (Out of Scope)
|
||
|
||
❌ 侧边栏面板方式 (非本方案范围)
|
||
❌ 搜索框内部图标注入 (已选择下方位置)
|
||
❌ 用户自定义面板尺寸 (固定800×600)
|
||
❌ 面板位置拖拽 (固定居中)
|
||
❌ 快捷键打开面板 (仅图标触发)
|
||
❌ 多标签页状态共享 (已选独立状态)
|
||
❌ 设置页面功能开关 (直接默认启用)
|
||
|
||
### 未来可能扩展 (Future Enhancements)
|
||
|
||
🔮 支持更多搜索引擎适配
|
||
🔮 面板位置自定义
|
||
🔮 快捷键打开支持
|
||
🔮 面板尺寸用户可调
|
||
🔮 拖拽移动面板
|
||
|
||
---
|
||
|
||
## 🚀 实现优先级
|
||
|
||
### P0 - 核心功能 (必须实现)
|
||
|
||
1. 触发图标注入和显示/隐藏逻辑
|
||
2. 模态遮罩层和面板容器
|
||
3. iframe 加载和消息通信
|
||
4. 搜索框填充功能
|
||
5. 搜索引擎适配配置 (Baidu、Google、Bing)
|
||
|
||
### P1 - 重要功能 (应该实现)
|
||
|
||
6. DOM 变化自动重注入
|
||
7. 焦点和悬停事件管理
|
||
8. 动画效果和过渡
|
||
9. 错误处理和容错
|
||
10. 国际化和主题适配
|
||
|
||
### P2 - 优化功能 (可以实现)
|
||
|
||
11. 性能优化 (懒加载、硬件加速)
|
||
12. 响应式布局适配
|
||
13. 焦点陷阱和键盘导航
|
||
14. 更多搜索引擎支持
|
||
|
||
---
|
||
|
||
## 📝 验收标准
|
||
|
||
### 功能验收
|
||
|
||
- [ ] 在 Baidu.com 搜索框下方能看到触发图标
|
||
- [ ] 在 Google.com 搜索框下方能看到触发图标
|
||
- [ ] 在 Bing.com 搜索框下方能看到触发图标
|
||
- [ ] 搜索框获得焦点时,图标自动显示
|
||
- [ ] 鼠标悬停在搜索区域时,图标自动显示
|
||
- [ ] 失去焦点且无悬停时,图标自动隐藏
|
||
- [ ] 点击图标后,显示居中的800×600模态面板
|
||
- [ ] 面板内正确加载 popup 组件
|
||
- [ ] 在面板中构建语法后,点击"应用"能填充到搜索框
|
||
- [ ] 点击遮罩层背景能关闭面板
|
||
- [ ] 按 ESC 键能关闭面板
|
||
- [ ] 面板打开后不受搜索框焦点影响
|
||
|
||
### 技术验收
|
||
|
||
- [ ] 使用 Shadow DOM 隔离样式
|
||
- [ ] 使用 iframe 加载 popup HTML
|
||
- [ ] 通过消息传递与 popup 通信
|
||
- [ ] 配置化适配器支持多搜索引擎
|
||
- [ ] MutationObserver 监听 DOM 变化
|
||
- [ ] 自动重注入功能正常工作
|
||
- [ ] 多个标签页独立运行,互不干扰
|
||
- [ ] 中英文界面正确显示
|
||
- [ ] 亮色/暗色主题正确适配
|
||
- [ ] 在 Chrome 91+ 版本正常运行
|
||
|
||
### 兼容性验收
|
||
|
||
- [ ] Chrome 91 浏览器测试通过
|
||
- [ ] Chrome 最新版本测试通过
|
||
- [ ] Windows 系统测试通过
|
||
- [ ] macOS 系统测试通过
|
||
- [ ] Linux 系统测试通过 (可选)
|
||
|
||
### 性能验收
|
||
|
||
- [ ] 图标显示/隐藏动画流畅 (>60fps)
|
||
- [ ] 面板打开/关闭动画流畅 (>60fps)
|
||
- [ ] 页面加载时注入不阻塞主线程
|
||
- [ ] 内存占用合理 (<10MB 增量)
|
||
- [ ] 不影响搜索引擎页面原有功能
|
||
|
||
---
|
||
|
||
## 🔗 相关文档
|
||
|
||
- [仓库上下文分析](./00-repository-context.md)
|
||
- [技术实现规格](./requirements-spec.md) (待生成)
|
||
- [代码实现](../../src/content/) (待开发)
|
||
|
||
---
|
||
|
||
## ✍️ 变更历史
|
||
|
||
| 版本 | 日期 | 变更内容 | 负责人 |
|
||
|------|------|---------|--------|
|
||
| v1.0 | 2025-11-20 | 初始版本,完成需求澄清 | requirements-pilot |
|
||
|
||
---
|
||
|
||
**文档状态**: ✅ 已确认,待用户审批进入实现阶段
|