123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- /**
- * 侧边栏管理类
- */
- 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 });
- // 监听来自sidebar的消息
- window.addEventListener("message", async (event) => {
- if (event.data.type === "COPY_TO_CLIPBOARD") {
- try {
- await navigator.clipboard.writeText(event.data.text);
- // 可选:发送成功消息回sidebar
- event.source.postMessage(
- {
- type: "COPY_SUCCESS",
- },
- "*"
- );
- } catch (err) {
- console.error("Failed to copy text:", err);
- // 可选:发送失败消息回sidebar
- event.source.postMessage(
- {
- type: "COPY_ERROR",
- error: err.message,
- },
- "*"
- );
- }
- }
- });
- }
- /**
- * 处理接收到的消息
- * @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;
- // 保存body的原始样式
- this.originalBodyStyle = {
- width: document.body.style.width,
- margin: document.body.style.margin,
- position: document.body.style.position,
- overflow: document.body.style.overflow,
- };
- // 包装内容
- 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;
- // 恢复body的原始样式
- if (this.originalBodyStyle) {
- Object.assign(document.body.style, this.originalBodyStyle);
- }
- while (wrapper.firstChild) {
- document.body.insertBefore(wrapper.firstChild, wrapper);
- }
- wrapper.remove();
- }
- /**
- * 发送页面信息到iframe
- */
- sendPageInfo() {
- const iframe = document.getElementById(this.sidebarId);
- if (!iframe) return;
- // 获取页面favicon
- let favicon = "";
- const iconLink = document.querySelector('link[rel*="icon"]');
- if (iconLink) {
- favicon = iconLink.href;
- } else {
- // 如果没有找到,使用网站根目录的favicon.ico
- const url = new URL(window.location.href);
- favicon = `${url.protocol}//${url.hostname}/favicon.ico`;
- }
- // 发送页面信息到iframe
- iframe.contentWindow.postMessage(
- {
- type: "PAGE_INFO",
- pageInfo: {
- favicon: favicon,
- title: document.title,
- url: window.location.href,
- },
- },
- "*"
- );
- }
- /**
- * 创建侧边栏
- */
- 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(() => {
- document.body.classList.add("sidebar-open");
- iframe.classList.add("show");
- });
- this.isOpen = true;
- await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, true);
- // 在iframe加载完成后发送页面信息
- iframe.onload = () => {
- this.sendPageInfo();
- };
- } catch (error) {
- console.error("Failed to create sidebar:", error);
- this.unwrapPageContent();
- }
- }
- /**
- * 移除侧边栏
- */
- async removeSidebar() {
- if (!this.isOpen) return;
- try {
- const sidebar = document.getElementById(this.sidebarId);
- if (sidebar) {
- document.body.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文件用于处理侧边栏内部的关闭按钮事件
|