diff --git a/docs/QUICK_REF.md b/docs/QUICK_REF.md
index fb1332e..c6e3114 100644
--- a/docs/QUICK_REF.md
+++ b/docs/QUICK_REF.md
@@ -7,7 +7,7 @@
## 版本速查
### 当前版本
-`0.1.5` → 下一目标 `0.1.6` ([M2.6](./M2.md))
+`0.1.6` → 下一目标 `0.1.7` ([M2.7](./M2.md))
### 模块版本范围
diff --git a/docs/README.md b/docs/README.md
index b25c9b7..5934bad 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -65,9 +65,9 @@ M11.10完成 → 1.0.0 (正式发布)
## 当前状态
-**当前版本**: `0.1.5`
-**当前进度**: 10/97 (10%)
-**下一任务**: [M2.6 基础面板组件](./M2.md#m26-基础面板组件--目标版本-0116)
+**当前版本**: `0.1.6`
+**当前进度**: 11/97 (11%)
+**下一任务**: [M2.7 面板位置计算](./M2.md#m27-面板位置计算--目标版本-0117)
---
diff --git a/docs/VERSION.md b/docs/VERSION.md
index 7ff5c59..8634369 100644
--- a/docs/VERSION.md
+++ b/docs/VERSION.md
@@ -29,12 +29,12 @@
| 任务 | 版本 | 描述 | 状态 | 日期 |
|------|------|------|------|------|
-| M2.1 | 0.1.1 | 文本选择检测 | ✅ | 2026-02-09 |
-| M2.2 | 0.1.2 | 获取选中文本坐标 | ✅ | 2026-02-09 |
-| M2.3 | 0.1.3 | 沙拉图标组件 | ✅ | 2026-02-09 |
-| M2.4 | 0.1.4 | 图标定位显示 | ✅ | 2026-02-09 |
-| M2.5 | 0.1.5 | 图标点击事件 | ✅ | 2026-02-09 |
-| M2.6 | 0.1.6 | 基础面板组件 | ⬜ | - |
+| M2.1 | 0.1.1 | 文本选择检测 | ✅ | 2026-02-10 |
+| M2.2 | 0.1.2 | 获取选中文本坐标 | ✅ | 2026-02-10 |
+| M2.3 | 0.1.3 | 沙拉图标组件 | ✅ | 2026-02-10 |
+| M2.4 | 0.1.4 | 图标定位显示 | ✅ | 2026-02-10 |
+| M2.5 | 0.1.5 | 图标点击事件 | ✅ | 2026-02-10 |
+| M2.6 | 0.1.6 | 基础面板组件 | ✅ | 2026-02-11 |
| M2.7 | 0.1.7 | 面板位置计算 | ⬜ | - |
| M2.8 | 0.1.8 | 图标-面板联动 | ⬜ | - |
| M2.9 | 0.1.9 | 图标显示开关 | ⬜ | - |
diff --git a/manifest.json b/manifest.json
index 7a9bf84..cd7e282 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "沙拉查词",
- "version": "0.1.5",
+ "version": "0.1.6",
"description": "聚合词典划词翻译",
"permissions": [
"storage",
diff --git a/package.json b/package.json
index 7778fd2..8057042 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salad-dict",
- "version": "0.1.5",
+ "version": "0.1.6",
"description": "聚合词典划词翻译",
"private": true,
"type": "module",
diff --git a/src/content/components/DictPanel.js b/src/content/components/DictPanel.js
new file mode 100644
index 0000000..8fb8302
--- /dev/null
+++ b/src/content/components/DictPanel.js
@@ -0,0 +1,129 @@
+/**
+ * @file 词典结果展示面板
+ * @description 显示词典查询结果的基础面板组件
+ */
+
+const PANEL_WIDTH = 400;
+const PANEL_HEIGHT = 300;
+
+/**
+ * 词典结果面板类
+ */
+export class DictPanel {
+ constructor() {
+ this.element = this.createElement();
+ }
+
+ /**
+ * 创建面板元素
+ * @returns {HTMLElement} 面板元素
+ */
+ createElement() {
+ // 创建容器
+ const container = document.createElement('div');
+ container.style.cssText = `
+ position: fixed;
+ z-index: 2147483646;
+ display: none;
+ width: ${PANEL_WIDTH}px;
+ height: ${PANEL_HEIGHT}px;
+ `;
+
+ // 创建 Shadow DOM
+ const shadow = container.attachShadow({ mode: 'open' });
+
+ // Shadow DOM 内容
+ shadow.innerHTML = `
+
+
+ `;
+
+ // 阻止点击事件冒泡
+ container.addEventListener('click', (e) => {
+ e.stopPropagation();
+ });
+
+ // 添加到页面
+ document.body.appendChild(container);
+
+ return container;
+ }
+
+ /**
+ * 显示面板在指定位置
+ * @param {number} x - X坐标
+ * @param {number} y - Y坐标
+ */
+ show(x, y) {
+ this.element.style.left = `${x}px`;
+ this.element.style.top = `${y}px`;
+ this.element.style.display = 'block';
+ }
+
+ /**
+ * 隐藏面板
+ */
+ hide() {
+ this.element.style.display = 'none';
+ }
+
+ /**
+ * 销毁组件
+ */
+ destroy() {
+ this.hide();
+ if (this.element.parentNode) {
+ this.element.parentNode.removeChild(this.element);
+ }
+ }
+}
+
+/**
+ * 创建并显示词典面板
+ * @param {number} x - X坐标
+ * @param {number} y - Y坐标
+ * @returns {DictPanel} 面板实例
+ */
+export function createDictPanel(x, y) {
+ const panel = new DictPanel();
+ panel.show(x, y);
+ return panel;
+}
diff --git a/src/content/selection.js b/src/content/selection.js
index 8a3bc88..98c53d8 100644
--- a/src/content/selection.js
+++ b/src/content/selection.js
@@ -5,8 +5,10 @@
import { logger } from './logger.js';
import { createSaladIcon } from './components/SaladIcon.js';
+import { DictPanel } from './components/DictPanel.js';
let currentIcon = null;
+let currentPanel = null;
/**
* 获取当前选中的文本
@@ -83,6 +85,16 @@ export function hasSelection() {
* @param {MouseEvent} event
*/
function handleMouseUp(event) {
+ // 如果点击的是当前图标元素,则直接返回
+ if (currentIcon && (currentIcon.element == event.target || currentIcon.element.contains(event.target))) {
+ return;
+ }
+
+ // 如果点击的是当前面板元素,则直接返回
+ if (currentPanel && (currentPanel.element == event.target || currentPanel.element.contains(event.target))) {
+ return;
+ }
+
// 延迟执行,等待选区完成
setTimeout(() => {
const selectedText = getSelectedText();
@@ -114,17 +126,31 @@ function handleMouseUp(event) {
* @param {number} y - Y坐标
*/
function showSaladIcon(x, y) {
- // 隐藏旧图标
+ // 隐藏旧图标和面板
if (currentIcon) {
currentIcon.destroy();
currentIcon = null;
}
+ if (currentPanel) {
+ currentPanel.destroy();
+ currentPanel = null;
+ }
// 创建新图标,传入点击回调
currentIcon = createSaladIcon(x, y, {
onClick: (event) => {
- logger.info('Icon click callback triggered');
- // 点击后图标不消失(后续再处理)
+ logger.info('Icon clicked, showing panel');
+
+ // 图标消失
+ if (currentIcon) {
+ currentIcon.destroy();
+ currentIcon = null;
+ }
+
+ // 显示面板(位置暂时写死 0,0,后续 M2.7 完善)
+ currentPanel = new DictPanel();
+ currentPanel.show(0, 0);
+ console.log('[SaladDict] Panel shown at (0, 0)');
}
});
@@ -132,7 +158,7 @@ function showSaladIcon(x, y) {
}
/**
- * 处理页面点击事件(用于隐藏图标)
+ * 处理页面点击事件(用于隐藏图标和面板)
* @param {MouseEvent} event
*/
function handleDocumentClick(event) {
@@ -142,6 +168,13 @@ function handleDocumentClick(event) {
currentIcon = null;
logger.info('Icon hidden by document click');
}
+
+ // 如果点击的不是面板元素,则隐藏面板
+ if (currentPanel && currentPanel.element !== event.target && !currentPanel.element.contains(event.target)) {
+ currentPanel.destroy();
+ currentPanel = null;
+ logger.info('Panel hidden by document click');
+ }
}
/**