# Mermaid Diagram Extension: ChatGPT

# 摘要

這是一個[瀏覽器擴充](https://github.com/aldosteroneyang/mermaid-extension)功能，用於增強 ChatGPT 網頁上的 Mermaid 圖表功能。主要特點包括自動渲染、放大鏡功能和編輯器整合。

![](https://raw.githubusercontent.com/aldosteroneyang/mermaid-extension/refs/heads/main/sample.png)

# 技術架構

擴充功能由以下核心檔案組成：

- **manifest.json**: 擴充功能配置檔案，定義版本、權限和功能
- **content.js**: 注入到目標網頁的內容腳本，負責初始化擴充功能
- **injected.js**: 處理 Mermaid 轉換和渲染邏輯的主要腳本
- **pako.min.js**: 資料壓縮庫，用於處理圖表代碼的壓縮和編碼

## 檔案關係

```
manifest.json  <-- 瀏覽器讀取的入口檔案
    |
    +--> content.js  <-- 注入到網頁
            |
            +--> injected.js  <-- 核心功能實現
            |
            +--> pako.min.js  <-- 壓縮庫依賴
```

# 運作流程

1. **初始化**:
   - content.js 將 injected.js 注入到 ChatGPT 頁面
   - 添加自定義 CSS 樣式，包括放大鏡和按鈕樣式
   - 載入 pako 壓縮庫
   - 設置事件攔截器，隱藏原始 Mermaid 代碼區塊

2. **圖表轉換**:
   - 識別頁面中的 Mermaid 代碼區塊 (`.language-mermaid`)
   - 使用 pako 壓縮代碼並轉為 Base64 編碼
   - 透過 mermaid.ink 服務生成圖片 URL
   - 建立圖片元素替換原始代碼區塊

3. **用戶界面增強**:
   - 替換原始代碼區塊為圖片
   - 添加放大鏡功能，支援 1.5 倍放大
   - 提供編輯器鏈接按鈕，直接跳轉到 mermaid.live 進行編輯
   - 添加錯誤處理和錯誤提示

4. **DOM 監控**:
   - 使用 MutationObserver 監控頁面變化
   - 當新的 Mermaid 區塊出現時自動進行轉換
   - 實現即時轉換無需頁面重載

# 安裝方法

1. 下載專案程式碼
2. 開啟 Chrome/Edge/Firefox 的擴充功能頁面
3. 啟用開發者模式
4. 點擊「載入未封裝的擴充功能」或「載入臨時附加元件」
5. 選擇專案資料夾
6. 前往 ChatGPT 網站進行測試

# 關鍵技術點

## 代碼轉換流程

```javascript
// 壓縮並編碼 Mermaid 代碼
function getEncodedPayload(mermaidCode) {
  // 添加灰色主題設定
  if (!mermaidCode.includes('%%{init:')) {
    mermaidCode = `%%{init: {'theme': 'default', 'themeVariables': { 'nodeBorder': '#c0c0c0', 'nodeBkg': '#e0e0e0' }}}%%\n${mermaidCode}`;
  }
  
  const payload = { 
    code: mermaidCode, 
    mermaid: { theme: "default" },
    updateEditor: true,
    autoSync: true
  };
  
  // 壓縮轉換
  const jsonStr = JSON.stringify(payload);
  const jsonUint8 = new TextEncoder().encode(jsonStr);
  const compressed = pako.deflate(jsonUint8);
  const binaryStr = Array.from(compressed)
    .map(byte => String.fromCharCode(byte))
    .join('');
  return btoa(binaryStr)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

// 取得圖片 URL
function getMermaidImageUrl(mermaidCode) {
  return "https://mermaid.ink/img/pako:" + getEncodedPayload(mermaidCode);
}

// 取得編輯器 URL
function getMermaidEditorUrl(mermaidCode) {
  return "https://mermaid.live/edit#pako:" + getEncodedPayload(mermaidCode);
}
```

## 放大鏡實現

擴充功能支援圖片放大鏡功能，可精確顯示游標位置的放大內容：

- 使用 CSS `background-position` 和 `background-size` 屬性控制顯示區域
- 通過鼠標事件 (mousemove) 計算放大區域位置
- 設置放大倍率為 1.5 倍
- 放大鏡大小為原圖的 50%

```javascript
// 放大鏡功能實現摘要
function setupMagnifier(imgElement, imgContainer) {
  const magnifier = document.createElement('div');
  magnifier.className = 'magnifier-glass';
  imgContainer.appendChild(magnifier);
  
  // 設置放大鏡樣式
  magnifier.style.backgroundImage = `url(${imgElement.src})`;
  magnifier.style.backgroundRepeat = 'no-repeat';
  magnifier.style.backgroundSize = `${imgElement.width * 1.5}px ${imgElement.height * 1.5}px`;
  
  // 鼠標移動時更新放大鏡位置
  imgElement.addEventListener('mousemove', function(e) {
    const rect = imgElement.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    
    // 計算放大鏡位置和背景位置
    const magnifierWidth = magnifier.offsetWidth;
    const magnifierHeight = magnifier.offsetHeight;
    
    let left = x - magnifierWidth / 2;
    let top = y - magnifierHeight / 2;
    
    // 限制在圖片範圍內
    left = Math.max(0, Math.min(left, imgElement.width - magnifierWidth));
    top = Math.max(0, Math.min(top, imgElement.height - magnifierHeight));
    
    magnifier.style.left = `${left}px`;
    magnifier.style.top = `${top}px`;
    
    // 計算背景位置
    const bgX = -(x * 1.5 - magnifierWidth / 2);
    const bgY = -(y * 1.5 - magnifierHeight / 2);
    magnifier.style.backgroundPosition = `${bgX}px ${bgY}px`;
  });
}
```

## DOM 監控與動態渲染

使用 MutationObserver 實現對頁面 DOM 變化的監控，當檢測到新的 Mermaid 代碼塊時自動執行轉換：

```javascript
// 設置 DOM 監控
function setupMutationObserver() {
  const observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
        // 處理新增的節點
        mutation.addedNodes.forEach(function(node) {
          if (node.nodeType === Node.ELEMENT_NODE) {
            // 查找並處理 Mermaid 代碼塊
            const mermaidBlocks = node.querySelectorAll('pre code.language-mermaid, .language-mermaid');
            if (mermaidBlocks.length > 0) {
              processMermaidBlocks(mermaidBlocks);
            }
          }
        });
      }
    });
  });
  
  // 開始監控整個文檔
  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
}
```

# Mermaid 圖表範例

以下是可透過此擴充功能渲染的圖表範例：

## 流程圖

```mermaid
graph TD
    A[開始] --> B{是否安裝擴充功能?}
    B -- 是 --> C[顯示圖表]
    B -- 否 --> D[顯示原始碼]
    C --> E[啟用放大鏡功能]
    D --> F[安裝擴充功能]
    F --> B
    E --> G[結束]
```

## 時序圖

```mermaid
sequenceDiagram
    participant User
    participant Extension
    participant ChatGPT
    participant MermaidInk
    
    User->>ChatGPT: 請求包含圖表的回應
    ChatGPT->>User: 返回包含Mermaid代碼的回應
    Extension->>Extension: 檢測Mermaid代碼塊
    Extension->>MermaidInk: 發送壓縮後的代碼
    MermaidInk->>Extension: 返回渲染的圖片URL
    Extension->>User: 顯示圖表及放大鏡功能
```

# 使用限制

- 僅支援 ChatGPT 網站 (chatgpt.com 及其子域名)
- 需依賴 mermaid.ink 服務進行圖表渲染，無法離線使用
- 放大鏡功能僅適用於已渲染的圖表
- 不支援交互式圖表功能
- 受限於 manifest v3 的網頁資源限制

# 常見問題排除

1. **圖表無法顯示**
   - 檢查網路連接是否正常
   - 確認 mermaid.ink 服務是否可用
   - 檢查 Mermaid 語法是否正確

2. **放大鏡功能無效**
   - 重新點擊圖表啟用放大功能
   - 刷新頁面後重試
   - 檢查瀏覽器擴充功能權限

3. **圖表顯示錯誤**
   - 點擊「在編輯器中開啟」按鈕檢查語法
   - 調整 Mermaid 語法後重新渲染

# 後續開發方向

1. 增加離線渲染支援，減少對 mermaid.ink 的依賴
2. 提供更多自定義選項，如圖表主題、顏色和字體大小
3. 支援更多網站和平台，如 Gmail、Google Docs 等
4. 優化圖表樣式和主題，提供暗色模式支援
5. 添加本地圖表儲存和匯出功能
6. 整合更多圖表類型和進階功能
7. 提供圖表編輯歷史記錄功能

# 相關資源

- [Mermaid 官方文檔](https://mermaid.js.org/)
- [Mermaid Live Editor](https://mermaid.live/)
- [Mermaid.ink API](https://mermaid.ink/)
- [Pako 壓縮庫](https://github.com/nodeca/pako) 
