浏览代码

feat: 发布v0.0.2版本

- 添加侧边栏动画效果
- 优化页面布局处理
- 改进状态管理
- 重构代码结构
- 修复已知问题
alibct 7 月之前
父节点
当前提交
8b861f3176
共有 9 个文件被更改,包括 520 次插入9 次删除
  1. 50 1
      CHANGELOG.md
  2. 73 0
      css/content.css
  3. 47 0
      css/sidebar.css
  4. 4 0
      js/background.js
  5. 243 0
      js/content.js
  6. 9 0
      js/sidebar.js
  7. 57 0
      js/utils.js
  8. 19 8
      manifest.json
  9. 18 0
      sidebar.html

+ 50 - 1
CHANGELOG.md

@@ -1 +1,50 @@
- 
+# 更新日志
+
+## [0.0.2] - 2024-03-xx
+
+### 新增
+
+- 实现侧边栏动画效果
+  - 平滑的滑入/滑出动画
+  - 内容区域过渡效果
+  - 优雅的淡入淡出效果
+
+### 优化
+
+- 改进页面布局处理机制
+- 优化状态管理和持久化
+- 提升代码可维护性
+  - 添加详细注释
+  - 实现模块化结构
+  - 增加错误处理
+
+### 修复
+
+- 修复页面导航后侧边栏状态丢失的问题
+- 修复页面布局偶发性错误的问题
+
+## [0.0.1] - 2024-03-xx
+
+### 新增
+
+- 初始化 Chrome 扩展项目
+- 创建基础项目结构
+- 设置基础 UI 界面
+- 实现侧边栏功能
+  - 3:1 分屏布局
+  - 页面内容自适应
+  - 状态持久化
+  - 窗口大小响应
+
+### 更改
+
+- 将项目名称设置为"派维斯智能体助手"
+- 设置初始版本号为 0.0.1
+- 更新项目描述为"一个辅助用户处理业务流程的智能体助手"
+
+### 优化
+
+- 代码模块化重构
+- 添加错误处理
+- 改进状态管理
+- 优化页面布局算法

+ 73 - 0
css/content.css

@@ -0,0 +1,73 @@
+/* 创建一个包装器来控制主页面内容 */
+#paiwise-main-wrapper {
+  position: fixed !important;
+  top: 0 !important;
+  left: 0 !important;
+  width: 100% !important;
+  height: 100vh !important;
+  overflow-y: auto !important;
+  overflow-x: hidden !important;
+  transition: width 0.3s ease-in-out !important;
+}
+
+/* 当侧边栏打开时的主内容样式 */
+#paiwise-main-wrapper.sidebar-open {
+  width: 75% !important;
+}
+
+#paiwise-sidebar {
+  position: fixed !important;
+  top: 0 !important;
+  right: -25% !important; /* 初始位置在屏幕外 */
+  width: 25% !important;
+  height: 100vh !important;
+  z-index: 2147483647 !important;
+  border: none !important;
+  background: #fff !important;
+  box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1) !important;
+  transition: right 0.3s ease-in-out !important;
+  opacity: 0;
+  transform: translateX(20px);
+}
+
+/* 当侧边栏显示时的样式 */
+#paiwise-sidebar.show {
+  right: 0 !important;
+  opacity: 1;
+  transform: translateX(0);
+}
+
+/* 隐藏原始滚动条 */
+body::-webkit-scrollbar {
+  display: none !important;
+}
+
+body {
+  margin: 0 !important;
+  padding: 0 !important;
+  overflow: hidden !important;
+  height: 100vh !important;
+}
+
+/* 添加动画关键帧 */
+@keyframes slideIn {
+  from {
+    opacity: 0;
+    transform: translateX(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateX(0);
+  }
+}
+
+@keyframes slideOut {
+  from {
+    opacity: 1;
+    transform: translateX(0);
+  }
+  to {
+    opacity: 0;
+    transform: translateX(20px);
+  }
+}

+ 47 - 0
css/sidebar.css

@@ -0,0 +1,47 @@
+html,
+body {
+  margin: 0;
+  padding: 0;
+  height: 100vh;
+  overflow: hidden;
+}
+
+body {
+  display: flex;
+  flex-direction: column;
+  background: #ffffff;
+}
+
+.sidebar-header {
+  padding: 16px;
+  border-bottom: 1px solid #eee;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.sidebar-header h1 {
+  margin: 0;
+  font-size: 18px;
+  color: #333;
+}
+
+.close-btn {
+  background: none;
+  border: none;
+  font-size: 24px;
+  color: #666;
+  cursor: pointer;
+  padding: 0 8px;
+  line-height: 1;
+}
+
+.close-btn:hover {
+  color: #333;
+}
+
+.sidebar-content {
+  flex: 1;
+  overflow-y: auto;
+  padding: 16px;
+}

+ 4 - 0
js/background.js

@@ -0,0 +1,4 @@
+// 监听扩展图标点击事件
+chrome.action.onClicked.addListener((tab) => {
+  chrome.tabs.sendMessage(tab.id, { action: "toggleSidebar" });
+});

+ 243 - 0
js/content.js

@@ -0,0 +1,243 @@
+/**
+ * 侧边栏管理类
+ */
+class SidebarManager {
+  /**
+   * @constant {Object} 配置常量
+   */
+  static CONFIG = {
+    SIDEBAR_ID: "paiwise-sidebar",
+    WRAPPER_ID: "paiwise-main-wrapper",
+    STORAGE_KEY: "sidebarOpen",
+  };
+
+  constructor() {
+    this.sidebarId = SidebarManager.CONFIG.SIDEBAR_ID;
+    this.wrapperId = SidebarManager.CONFIG.WRAPPER_ID;
+    this.isOpen = false;
+    this.init();
+  }
+
+  /**
+   * 初始化侧边栏
+   */
+  async init() {
+    try {
+      await this.checkAndRestoreState();
+      this.setupEventListeners();
+    } catch (error) {
+      console.error("Sidebar initialization failed:", error);
+    }
+  }
+
+  /**
+   * 设置事件监听器
+   */
+  setupEventListeners() {
+    // 监听页面加载完成事件
+    window.addEventListener("load", () => this.checkAndRestoreState());
+
+    // 监听来自 sidebar 的消息
+    window.addEventListener("message", this.handleMessage.bind(this));
+
+    // 监听窗口大小变化
+    window.addEventListener(
+      "resize",
+      Utils.debounce(() => this.handleResize(), 100)
+    );
+
+    // 监听页面可见性变化
+    document.addEventListener("visibilitychange", () => {
+      if (!document.hidden) {
+        this.checkAndRestoreState();
+      }
+    });
+
+    // 监听 history 变化
+    window.addEventListener("popstate", () => {
+      this.checkAndRestoreState();
+    });
+
+    // 监听页面 URL 变化
+    let lastUrl = location.href;
+    new MutationObserver(() => {
+      const url = location.href;
+      if (url !== lastUrl) {
+        lastUrl = url;
+        this.checkAndRestoreState();
+      }
+    }).observe(document, { subtree: true, childList: true });
+  }
+
+  /**
+   * 处理接收到的消息
+   * @param {MessageEvent} event
+   */
+  handleMessage(event) {
+    if (event.data.action === "closeSidebar") {
+      this.removeSidebar();
+    }
+  }
+
+  /**
+   * 处理窗口大小变化
+   */
+  handleResize() {
+    const sidebar = document.getElementById(this.sidebarId);
+    if (sidebar && this.isOpen) {
+      sidebar.style.height = `${window.innerHeight}px`;
+    }
+  }
+
+  /**
+   * 检查并恢复侧边栏状态
+   */
+  async checkAndRestoreState() {
+    try {
+      const isOpen = await Utils.getStorageData(
+        SidebarManager.CONFIG.STORAGE_KEY
+      );
+      if (isOpen && !this.isOpen) {
+        // 只有当应该打开且当前未打开时才创建
+        this.createSidebar();
+      } else if (!isOpen && this.isOpen) {
+        // 只有当应该关闭且当前打开时才移除
+        this.removeSidebar();
+      }
+    } catch (error) {
+      console.error("Failed to restore sidebar state:", error);
+    }
+  }
+
+  /**
+   * 包装页面内容
+   */
+  wrapPageContent() {
+    if (document.getElementById(this.wrapperId)) return;
+
+    const wrapper = document.createElement("div");
+    wrapper.id = this.wrapperId;
+
+    // 添加初始样式
+    wrapper.style.width = "100%";
+
+    while (document.body.firstChild) {
+      if (document.body.firstChild.id !== this.sidebarId) {
+        wrapper.appendChild(document.body.firstChild);
+      } else {
+        document.body.removeChild(document.body.firstChild);
+      }
+    }
+
+    document.body.appendChild(wrapper);
+  }
+
+  /**
+   * 解除页面内容包装
+   */
+  unwrapPageContent() {
+    const wrapper = document.getElementById(this.wrapperId);
+    if (!wrapper) return;
+
+    while (wrapper.firstChild) {
+      document.body.insertBefore(wrapper.firstChild, wrapper);
+    }
+
+    wrapper.remove();
+  }
+
+  /**
+   * 创建侧边栏
+   */
+  async createSidebar() {
+    if (document.getElementById(this.sidebarId)) return;
+
+    try {
+      this.wrapPageContent();
+
+      const iframe = document.createElement("iframe");
+      iframe.id = this.sidebarId;
+      iframe.src = chrome.runtime.getURL("sidebar.html");
+      document.body.appendChild(iframe);
+
+      // 使用 RAF 确保 DOM 更新后再添加动画类
+      requestAnimationFrame(() => {
+        const wrapper = document.getElementById(this.wrapperId);
+        const sidebar = document.getElementById(this.sidebarId);
+
+        // 先触发重排
+        sidebar.getBoundingClientRect();
+
+        // 添加动画类
+        if (wrapper) wrapper.classList.add("sidebar-open");
+        if (sidebar) sidebar.classList.add("show");
+      });
+
+      this.isOpen = true;
+      await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, true);
+    } catch (error) {
+      console.error("Failed to create sidebar:", error);
+      this.unwrapPageContent();
+    }
+  }
+
+  /**
+   * 移除侧边栏
+   */
+  async removeSidebar() {
+    if (!this.isOpen) return;
+
+    try {
+      const sidebar = document.getElementById(this.sidebarId);
+      const wrapper = document.getElementById(this.wrapperId);
+
+      if (sidebar && wrapper) {
+        // 移除动画类
+        wrapper.classList.remove("sidebar-open");
+        sidebar.classList.remove("show");
+
+        // 等待动画完成后再移除元素
+        await new Promise((resolve) => {
+          sidebar.addEventListener(
+            "transitionend",
+            () => {
+              this.unwrapPageContent();
+              sidebar.remove();
+              resolve();
+            },
+            { once: true }
+          );
+        });
+      }
+
+      this.isOpen = false;
+      await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, false);
+    } catch (error) {
+      console.error("Failed to remove sidebar:", error);
+    }
+  }
+
+  /**
+   * 切换侧边栏显示状态
+   */
+  toggle() {
+    if (this.isOpen) {
+      this.removeSidebar();
+    } else {
+      this.createSidebar();
+    }
+  }
+}
+
+// 初始化侧边栏管理器
+const sidebarManager = new SidebarManager();
+
+// 监听来自背景脚本的消息
+chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+  if (message.action === "toggleSidebar") {
+    sidebarManager.toggle();
+    sendResponse({ success: true });
+  }
+});
+
+// 创建一个新的js文件用于处理侧边栏内部的关闭按钮事件

+ 9 - 0
js/sidebar.js

@@ -0,0 +1,9 @@
+document.addEventListener("DOMContentLoaded", function () {
+  // 处理关闭按钮点击事件
+  document
+    .getElementById("close-sidebar")
+    .addEventListener("click", function () {
+      // 通知content script关闭侧边栏
+      window.parent.postMessage({ action: "closeSidebar" }, "*");
+    });
+});

+ 57 - 0
js/utils.js

@@ -0,0 +1,57 @@
+/**
+ * 工具类
+ */
+class Utils {
+  /**
+   * 防抖函数
+   * @param {Function} func 要执行的函数
+   * @param {number} wait 等待时间
+   * @returns {Function}
+   */
+  static debounce(func, wait) {
+    let timeout;
+    return function executedFunction(...args) {
+      const later = () => {
+        clearTimeout(timeout);
+        func(...args);
+      };
+      clearTimeout(timeout);
+      timeout = setTimeout(later, wait);
+    };
+  }
+
+  /**
+   * 安全地获取存储数据
+   * @param {string} key
+   * @returns {Promise}
+   */
+  static async getStorageData(key) {
+    try {
+      return new Promise((resolve) => {
+        chrome.storage.local.get([key], (result) => {
+          resolve(result[key]);
+        });
+      });
+    } catch (error) {
+      console.error("Storage access error:", error);
+      return null;
+    }
+  }
+
+  /**
+   * 安全地设置存储数据
+   * @param {string} key
+   * @param {any} value
+   * @returns {Promise}
+   */
+  static async setStorageData(key, value) {
+    try {
+      return new Promise((resolve) => {
+        chrome.storage.local.set({ [key]: value }, resolve);
+      });
+    } catch (error) {
+      console.error("Storage write error:", error);
+      return false;
+    }
+  }
+}

+ 19 - 8
manifest.json

@@ -1,20 +1,31 @@
 {
   "manifest_version": 3,
   "name": "派维斯智能体助手",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "description": "一个辅助用户处理业务流程的智能体助手",
-  "permissions": [],
+  "author": "Paiwise Team",
+  "permissions": ["activeTab", "scripting", "storage", "tabs"],
   "action": {
-    "default_popup": "popup.html",
     "default_icon": {
       "16": "images/icon16.png",
       "48": "images/icon48.png",
       "128": "images/icon128.png"
     }
   },
-  "icons": {
-    "16": "images/icon16.png",
-    "48": "images/icon48.png",
-    "128": "images/icon128.png"
-  }
+  "background": {
+    "service_worker": "js/background.js"
+  },
+  "content_scripts": [
+    {
+      "matches": ["<all_urls>"],
+      "css": ["css/content.css"],
+      "js": ["js/utils.js", "js/content.js"]
+    }
+  ],
+  "web_accessible_resources": [
+    {
+      "resources": ["sidebar.html", "css/*", "js/*"],
+      "matches": ["<all_urls>"]
+    }
+  ]
 }

+ 18 - 0
sidebar.html

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8" />
+    <title>派维斯智能体助手</title>
+    <link rel="stylesheet" href="css/sidebar.css" />
+  </head>
+  <body>
+    <div class="sidebar-header">
+      <h1>派维斯智能体助手</h1>
+      <button id="close-sidebar" class="close-btn">×</button>
+    </div>
+    <div class="sidebar-content">
+      <!-- 这里将放置助手的主要功能界面 -->
+    </div>
+    <script src="js/sidebar.js"></script>
+  </body>
+</html>