Docker Hub 镜像下载器

在Docker Hub页面添加下载按钮,实现离线镜像下载功能

当前为 2025-06-17 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Docker Hub 镜像下载器
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  在Docker Hub页面添加下载按钮,实现离线镜像下载功能
// @author       X6nux
// @match        https://hub.docker.com/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @require      https://update.greasyfork.org/scripts/539732/1609156/tarballjs.js
// @license      MIT
// ==/UserScript==

/**
 * Docker Hub 镜像下载器油猴脚本
 * 
 * 文件说明:
 * 本脚本是一个Tampermonkey用户脚本,用于在Docker Hub网站上添加镜像下载功能。
 * 它可以直接在浏览器中下载Docker镜像并组装成标准的TAR格式文件。
 * 
 * 主要功能:
 * 1. 在Docker Hub镜像页面自动添加下载器界面
 * 2. 支持多种CPU架构选择(amd64、arm64等)
 * 3. 实现分层下载和前端组装
 * 4. 生成标准的Docker TAR文件
 * 5. 支持架构自动检测
 * 
 * 安装使用:
 * 1. 安装Tampermonkey浏览器插件
 * 2. 点击安装此脚本
 * 3. 访问任意Docker Hub镜像页面
 * 4. 使用页面顶部的下载器工具
 */

(function() {
    'use strict';

    // ==================== 全局变量和配置 ====================
    
    let downloadInProgress = false; // 下载进程状态标志
    let manifestData = null; // 镜像清单数据存储
    let downloadedLayers = new Map(); // 已下载镜像层的映射表
    let tempLayerCache = new Map(); // 临时层数据缓存
    let db = null; // IndexedDB数据库实例
    let cachedArchitectures = null; // 缓存的架构信息,包含架构和对应的SHA256
    let useTemporaryCache = false; // 是否使用临时缓存(minimal模式)
    let selectedMemoryMode = 'minimal'; // 内存模式选择,直接使用最小内存模式
    let downloadProgressMap = new Map(); // 实时下载进度映射
    let progressUpdateInterval = null; // 进度更新定时器

    // API服务器配置
    const API_BASE_URL = 'https://registry.lfree.org/api';

    // ==================== 样式定义 ====================
    
    /**
     * 添加自定义CSS样式到页面
     * 功能:定义下载器界面的所有视觉样式
     */
    function addCustomStyles() {
        GM_addStyle(`
            /* 主要下载按钮样式 - 内联版本 */
            .docker-download-btn {
                background: linear-gradient(145deg, #28a745, #218838);
                color: white;
                border: 2px solid #28a745;
                padding: 8px 16px;
                border-radius: 6px;
                font-size: 13px;
                font-weight: 600;
                cursor: pointer;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                display: inline-flex;
                align-items: center;
                gap: 6px;
                margin: 0 8px 0 0;
                box-shadow: 0 2px 8px rgba(40, 167, 69, 0.2);
                text-decoration: none;
                position: relative;
                overflow: hidden;
                vertical-align: middle;
            }

            /* 按钮悬停效果 */
            .docker-download-btn:hover {
                background: linear-gradient(145deg, #218838, #1e7e34);
                transform: translateY(-2px);
                box-shadow: 0 6px 20px rgba(40, 167, 69, 0.4);
                color: white;
                text-decoration: none;
            }

            /* 按钮禁用状态 */
            .docker-download-btn:disabled {
                background: linear-gradient(145deg, #6c757d, #5a6268);
                cursor: not-allowed;
                transform: none;
                animation: downloadProgress 2s infinite linear;
            }

            /* 架构选择下拉框样式 - 内联版本 */
            .arch-selector {
                margin: 0 8px 0 0;
                padding: 6px 10px;
                border: 1px solid #007bff;
                border-radius: 4px;
                background: white;
                font-size: 12px;
                min-width: 120px;
                vertical-align: middle;
                cursor: pointer;
            }

            .arch-selector:hover {
                border-color: #0056b3;
                box-shadow: 0 2px 4px rgba(0, 123, 255, 0.25);
            }

            .arch-selector:focus {
                outline: none;
                border-color: #0056b3;
                box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
            }

            /* 移除进度显示区域样式,改为在按钮上显示 */

            /* 下载进度动画 */
            @keyframes downloadProgress {
                0% { background-position: -100% 0; }
                100% { background-position: 100% 0; }
            }

            /* 内联按钮容器样式 */
            .docker-download-container {
                display: inline-flex !important;
                align-items: center !important;
                gap: 8px !important;
                margin: 0 10px !important;
                vertical-align: middle !important;
                background: none !important;
                border: none !important;
                padding: 0 !important;
                box-shadow: none !important;
                font-family: inherit !important;
            }

            /* 检测架构按钮特殊样式 */
            .detect-arch-btn {
                background: linear-gradient(145deg, #6f42c1, #5a32a3);
                border-color: #6f42c1;
            }

            .detect-arch-btn:hover {
                background: linear-gradient(145deg, #5a32a3, #4e2a87);
                border-color: #5a32a3;
            }

            /* 响应式设计 */
            @media (max-width: 768px) {
                .control-row {
                    flex-direction: column;
                    align-items: stretch;
                }
                
                .docker-download-btn,
                .arch-selector {
                    width: 100%;
                    margin: 5px 0;
                }
            }
        `);
    }

    // ==================== 数据存储管理 ====================

    /**
     * 初始化IndexedDB数据库
     * 功能:创建和配置用于存储镜像层数据的本地数据库
     * @returns {Promise} 数据库初始化完成的Promise
     */
    function initIndexedDB() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open('DockerImageStore', 1);

            // 数据库连接失败处理
            request.onerror = () => reject(request.error);
            
            // 数据库连接成功处理
            request.onsuccess = () => {
                db = request.result;
                resolve();
            };

            // 数据库结构升级处理
            request.onupgradeneeded = (event) => {
                const database = event.target.result;

                // 创建镜像层存储表
                if (!database.objectStoreNames.contains('layers')) {
                    const layerStore = database.createObjectStore('layers', { keyPath: 'digest' });
                    layerStore.createIndex('imageName', 'imageName', { unique: false });
                }

                // 创建镜像清单存储表
                if (!database.objectStoreNames.contains('manifests')) {
                    const manifestStore = database.createObjectStore('manifests', { keyPath: 'key' });
                }
            };
        });
    }

    // ==================== 页面信息提取 ====================

    /**
     * 从当前Docker Hub页面提取镜像信息
     * 功能:自动识别并提取镜像名称和标签信息
     * @returns {Object} 包含imageName和imageTag的对象
     */
    function extractImageInfo() {
        const url = window.location.pathname;
        const pathParts = url.split('/').filter(part => part);

        let imageName = '';
        let imageTag = 'latest';

        // Docker Hub URL格式分析和处理
        // 支持的格式:
        // - 官方镜像: /r/nginx, /_/nginx
        // - 用户镜像: /r/username/imagename
        // - 组织镜像: /r/organization/imagename
        // - 镜像层页面: /layers/username/imagename/tag/images/sha256-...
        
        // 解析URL路径段
        
        if (pathParts.length >= 4 && pathParts[0] === 'layers') {
            // 处理 layers 页面格式: /layers/username/imagename/tag/...
            const namespace = pathParts[1];
            const repoName = pathParts[2];
            const tag = pathParts[3];
            
            if (namespace === '_') {
                // 官方镜像: /layers/_/nginx/latest/...
                imageName = repoName;
            } else {
                // 用户/组织镜像: /layers/username/imagename/tag/...
                imageName = namespace + '/' + repoName;
            }
            imageTag = tag;
            
            // 从layers URL提取镜像信息
            
        } else if (pathParts.length >= 2 && pathParts[0] === 'r') {
            // 处理 /r/ 路径格式
            if (pathParts.length === 2) {
                // 官方镜像: /r/nginx
                imageName = pathParts[1];
            } else if (pathParts.length >= 3) {
                // 用户/组织镜像: /r/username/imagename
                imageName = pathParts[1] + '/' + pathParts[2];
            }
            
            // 检查是否有tags路径来提取特定标签
            if (pathParts.includes('tags') && pathParts.length > pathParts.indexOf('tags') + 1) {
                const tagIndex = pathParts.indexOf('tags') + 1;
                imageTag = pathParts[tagIndex];
            }
            
            // 从/r/ URL提取镜像信息
            
        } else if (pathParts.length >= 2 && pathParts[0] === '_') {
            // 官方镜像的另一种格式: /_/nginx
            imageName = pathParts[1];
            // 从/_/ URL提取镜像信息
            
        } else if (pathParts.length >= 2) {
            // 其他格式的通用处理(保留原有逻辑作为备用)
            imageName = pathParts[0] + '/' + pathParts[1];
            // 通用格式提取镜像信息
        }

        // 从页面DOM元素提取信息作为备用方案
        if (!imageName) {
            // 尝试从页面标题获取镜像名称
            const titleSelectors = [
                'h1[data-testid="repository-title"]',
                '.RepositoryNameHeader__repositoryName',
                'h1.repository-title',
                '.repository-name'
            ];
            
            for (const selector of titleSelectors) {
                const titleElement = document.querySelector(selector);
                if (titleElement) {
                    imageName = titleElement.textContent.trim();
                    break;
                }
            }
            
            // 尝试从面包屑导航获取
            if (!imageName) {
                const breadcrumbLinks = document.querySelectorAll('[data-testid="breadcrumb"] a, .breadcrumb a');
                if (breadcrumbLinks.length >= 2) {
                    imageName = breadcrumbLinks[breadcrumbLinks.length - 1].textContent.trim();
                }
            }
        }

        // 提取标签信息
        const tagSelectors = [
            '[data-testid="tag-name"]',
            '.tag-name',
            '.current-tag',
            '.active-tag'
        ];
        
        for (const selector of tagSelectors) {
            const tagElement = document.querySelector(selector);
            if (tagElement) {
                const tagText = tagElement.textContent.trim();
                imageTag = tagText.replace(':', '').replace('Tag: ', '');
                break;
            }
        }

        // 从URL查询参数获取标签信息
        const urlParams = new URLSearchParams(window.location.search);
        const tagFromUrl = urlParams.get('tag');
        if (tagFromUrl) {
            imageTag = tagFromUrl;
        }

        // 最终结果处理和修正
        if (imageName) {
            // 对于官方镜像(不包含斜杠的镜像名),添加library前缀
            if (!imageName.includes('/') && imageName !== '') {
                imageName = 'library/' + imageName;
                // 添加library前缀
            }
        }

        console.log('最终提取的镜像信息:', { imageName, imageTag, url });
        return { imageName, imageTag };
    }

    // ==================== UI界面创建 ====================

    /**
     * 创建内联下载按钮
     * 功能:生成直接集成到Docker Hub界面中的下载按钮
     * @returns {HTMLElement} 下载按钮DOM元素
     */
    function createInlineDownloadButton() {
        const button = document.createElement('button');
        button.className = 'docker-download-btn';
        button.id = 'downloadBtn';
        button.innerHTML = '🚀 下载镜像';
        button.title = '点击下载Docker镜像';
        
        return button;
    }

    /**
     * 创建架构选择下拉框
     * 功能:生成紧凑的架构选择器,支持自动架构检测
     * @returns {HTMLElement} 架构选择器DOM元素
     */
    function createArchSelector() {
        const select = document.createElement('select');
        select.className = 'arch-selector';
        select.id = 'archSelector';
        select.title = '选择目标架构(将自动检测页面架构)';
        select.innerHTML = `
            <option value="">自动检测</option>
            <option value="linux/amd64">linux/amd64</option>
            <option value="linux/arm64">linux/arm64</option>
            <option value="linux/arm/v7">linux/arm/v7</option>
            <option value="linux/arm/v6">linux/arm/v6</option>
            <option value="linux/386">linux/386</option>
            <option value="windows/amd64">windows/amd64</option>
        `;
        
        // 异步设置自动检测的架构和更新可用架构列表
        setTimeout(async () => {
            const indicator = document.getElementById('archIndicator');
            try {
                // 显示检测状态
                if (indicator) {
                    indicator.style.display = 'inline';
                    indicator.textContent = '🔍 获取架构列表...';
                }
                
                // 首先获取镜像的可用架构列表
                const availableArchs = await getAvailableArchitectures();
                if (availableArchs && availableArchs.length > 0) {
                    // 更新架构选择器选项
                    updateArchSelectorOptions(select, availableArchs);
                    addLog(`已更新架构选择器,包含 ${availableArchs.length} 个可用架构`);
                    
                    // 架构信息已更新到选择器中
                    
                    if (indicator) {
                        indicator.textContent = '🔍 检测当前架构...';
                    }
                }
                
                // 然后检测当前页面的架构
                const detectedArch = await autoDetectArchitecture();
                if (detectedArch) {
                    // 检查选项中是否存在检测到的架构
                    const existingOption = select.querySelector(`option[value="${detectedArch}"]`);
                    if (existingOption) {
                        select.value = detectedArch;
                        addLog(`架构选择器已设置为检测到的架构: ${detectedArch}`);
                    } else {
                        // 如果选项中不存在,添加新选项
                        const newOption = document.createElement('option');
                        newOption.value = detectedArch;
                        newOption.textContent = detectedArch;
                        newOption.selected = true;
                        select.appendChild(newOption);
                        addLog(`已添加并选择检测到的架构: ${detectedArch}`);
                    }
                    
                    // 更新选择器标题显示当前检测到的架构
                    select.title = `当前架构: ${detectedArch} (自动检测)`;
                    
                    // 更新状态指示器
                    if (indicator) {
                        indicator.textContent = `✅ ${detectedArch}`;
                        indicator.style.color = '#28a745';
                        setTimeout(() => {
                            indicator.style.display = 'none';
                        }, 3000);
                    }
                } else {
                    // 检测失败时的处理
                    if (indicator) {
                        indicator.textContent = '❌ 检测失败';
                        indicator.style.color = '#dc3545';
                        setTimeout(() => {
                            indicator.style.display = 'none';
                        }, 3000);
                    }
                }
            } catch (error) {
                addLog(`自动架构检测失败: ${error.message}`, 'error');
                if (indicator) {
                    indicator.textContent = '❌ 检测失败';
                    indicator.style.color = '#dc3545';
                    setTimeout(() => {
                        indicator.style.display = 'none';
                    }, 3000);
                }
            }
        }, 1000); // 延迟1秒等待页面完全加载
        
        return select;
    }



    /**
     * 创建检测架构按钮
     * 功能:生成架构检测按钮
     * @returns {HTMLElement} 检测按钮DOM元素
     */
    function createDetectArchButton() {
        const button = document.createElement('button');
        button.className = 'docker-download-btn detect-arch-btn';
        button.id = 'detectArchBtn';
        button.innerHTML = '🔍 检测架构';
        button.title = '检测镜像支持的架构';
        
        return button;
    }

    // 移除进度显示区域创建函数,改为在按钮上显示进度

    // ==================== 日志和工具函数 ====================

    /**
     * 添加日志信息(仅在控制台显示)
     * 功能:在控制台记录下载进度和状态信息
     * @param {string} message - 要显示的日志消息
     * @param {string} type - 日志类型(info, error, success)
     */
    function addLog(message, type = 'info') {
        console.log('Docker Downloader:', message);
    }

    /**
     * 更新下载按钮上的进度显示(增强版)
     * 功能:在下载按钮上显示详细的下载信息和进度
     * @param {string} text - 要显示的文本
     * @param {string} status - 状态类型(downloading, complete, error)
     * @param {Object} details - 详细信息对象
     */
    function updateButtonProgress(text, status = 'downloading', details = {}) {
        const downloadBtn = document.getElementById('downloadBtn');
        if (!downloadBtn) return;

        // 构建详细的按钮文本
        let buttonText = text;
        
        // 如果有详细信息,添加到按钮文本中
        if (details.progress !== undefined) {
            buttonText += ` ${details.progress}%`;
        }
        
        if (details.speed && details.speed > 0) {
            buttonText += ` (${formatSpeed(details.speed)})`;
        }
        
        if (details.current && details.total) {
            buttonText += ` [${details.current}/${details.total}]`;
        }
        
        if (details.size && status !== 'downloading') {
            buttonText += ` ${formatSize(details.size)}`;
        }

        downloadBtn.textContent = buttonText;
        
        // 根据状态设置按钮样式
        switch (status) {
            case 'downloading':
                downloadBtn.style.background = 'linear-gradient(145deg, #007bff, #0056b3)';
                downloadBtn.style.color = 'white';
                downloadBtn.disabled = true;
                break;
            case 'complete':
                downloadBtn.style.background = 'linear-gradient(145deg, #28a745, #218838)';
                downloadBtn.style.color = 'white';
                downloadBtn.disabled = false;
                setTimeout(() => {
                    downloadBtn.textContent = '🚀 下载镜像';
                    downloadBtn.style.background = 'linear-gradient(145deg, #28a745, #218838)';
                }, 3000);
                break;
            case 'error':
                downloadBtn.style.background = 'linear-gradient(145deg, #dc3545, #c82333)';
                downloadBtn.style.color = 'white';
                downloadBtn.disabled = false;
                setTimeout(() => {
                    downloadBtn.textContent = '🚀 下载镜像';
                    downloadBtn.style.background = 'linear-gradient(145deg, #28a745, #218838)';
                }, 3000);
                break;
            case 'analyzing':
                downloadBtn.style.background = 'linear-gradient(145deg, #6f42c1, #5a32a3)';
                downloadBtn.style.color = 'white';
                downloadBtn.disabled = true;
                break;
            case 'assembling':
                downloadBtn.style.background = 'linear-gradient(145deg, #fd7e14, #e8690b)';
                downloadBtn.style.color = 'white';
                downloadBtn.disabled = true;
                break;
            default:
                downloadBtn.style.background = 'linear-gradient(145deg, #28a745, #218838)';
                downloadBtn.style.color = 'white';
                downloadBtn.disabled = false;
        }
    }

    /**
     * 智能选择内存策略
     * 功能:根据镜像大小和用户设置选择最适合的内存模式
     */
    function chooseMemoryStrategy() {
        if (!manifestData) return;
        
        const totalSize = manifestData.totalSize;
        
        addLog(`📊 镜像总大小: ${formatSize(totalSize)}`);
        
        // 根据用户选择的模式和镜像大小确定存储策略
        if (selectedMemoryMode === 'minimal') {
            useTemporaryCache = true;
            addLog(`💾 最小内存模式:所有层数据使用临时缓存`);
        } else if (selectedMemoryMode === 'normal') {
            useTemporaryCache = false;
            addLog(`💾 标准模式:所有层数据使用IndexedDB存储`);
        } else if (selectedMemoryMode === 'stream') {
            useTemporaryCache = totalSize > 200 * 1024 * 1024; // 200MB阈值
            if (useTemporaryCache) {
                addLog(`🌊 流式模式:镜像较大 (${formatSize(totalSize)}),使用临时缓存避免IndexedDB限制`);
            } else {
                addLog(`🌊 流式模式:镜像较小 (${formatSize(totalSize)}),使用IndexedDB存储`);
            }
        } else if (selectedMemoryMode === 'auto') {
            // 自动模式根据镜像总大小智能选择
            if (totalSize > 1024 * 1024 * 1024) { // 1GB+
                useTemporaryCache = true;
                addLog(`🤖 自动模式:检测到超大镜像 (${formatSize(totalSize)}),自动选择临时缓存模式避免OOM`);
            } else if (totalSize > 500 * 1024 * 1024) { // 500MB+
                useTemporaryCache = true;
                addLog(`🤖 自动模式:检测到大镜像 (${formatSize(totalSize)}),自动选择临时缓存模式`);
            } else {
                useTemporaryCache = false;
                addLog(`🤖 自动模式:检测到中小镜像 (${formatSize(totalSize)}),自动选择标准存储模式`);
            }
        }
        
        // 额外的智能提示
        if (totalSize > 2 * 1024 * 1024 * 1024) { // 2GB+
            addLog(`⚠️ 超大镜像警告: ${formatSize(totalSize)} - 建议使用最小内存模式,确保设备有足够内存`);
        } else if (totalSize > 1024 * 1024 * 1024) { // 1GB+
            addLog(`⚠️ 大镜像提示: ${formatSize(totalSize)} - 下载可能耗时较长,请保持网络连接稳定`);
        }
    }

    /**
     * 启动实时进度更新
     * 功能:定期更新下载进度显示,提供流畅的用户体验
     */
    function startRealTimeProgressUpdate() {
        if (progressUpdateInterval) {
            clearInterval(progressUpdateInterval);
        }
        
        progressUpdateInterval = setInterval(() => {
            updateRealTimeProgress();
        }, 500); // 每500ms更新一次进度
    }

    /**
     * 停止实时进度更新
     * 功能:清理进度更新定时器
     */
    function stopRealTimeProgressUpdate() {
        if (progressUpdateInterval) {
            clearInterval(progressUpdateInterval);
            progressUpdateInterval = null;
        }
    }

    /**
     * 更新实时进度显示
     * 功能:计算并显示当前的总体下载进度
     */
    function updateRealTimeProgress() {
        if (!manifestData || downloadProgressMap.size === 0) return;

        let totalDownloaded = 0;
        let totalSpeed = 0;
        let completedLayers = 0;
        
        // 统计所有层的下载进度
        for (const [digest, progressInfo] of downloadProgressMap.entries()) {
            totalDownloaded += progressInfo.downloaded;
            totalSpeed += progressInfo.speed;
            if (progressInfo.completed) {
                completedLayers++;
            }
        }
        
        const totalSize = manifestData.totalSize;
        const progress = totalSize > 0 ? Math.min(Math.round((totalDownloaded / totalSize) * 100), 100) : 0;
        
        // 更新按钮显示
        updateButtonProgress('⬇️ 下载镜像层', 'downloading', {
            progress: progress,
            current: completedLayers,
            total: manifestData.layers.length,
            speed: totalSpeed
        });
    }

    /**
     * 格式化文件大小显示
     * 功能:将字节数转换为人类可读的格式(B, KB, MB, GB)
     * @param {number} bytes - 要格式化的字节数
     * @returns {string} 格式化后的大小字符串
     */
    function formatSize(bytes) {
        const sizes = ['B', 'KB', 'MB', 'GB'];
        if (bytes === 0) return '0 B';
        const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
        return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
    }

    /**
     * 格式化下载速度
     * 功能:将字节/秒转换为可读的速度单位
     * @param {number} bytesPerSecond - 字节/秒
     * @returns {string} 格式化的速度字符串
     */
    function formatSpeed(bytesPerSecond) {
        if (bytesPerSecond === 0) return '0 B/s';
        const sizes = ['B/s', 'KB/s', 'MB/s', 'GB/s'];
        const i = parseInt(Math.floor(Math.log(bytesPerSecond) / Math.log(1024)));
        return Math.round(bytesPerSecond / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
    }

    /**
     * 创建镜像信息展示区域
     * 功能:创建类似download.html的镜像信息展示卡片
     * @returns {HTMLElement} 镜像信息展示DOM元素
     */
    function createImageInfoDisplay() {
        const infoContainer = document.createElement('div');
        infoContainer.id = 'dockerImageInfo';
        infoContainer.className = 'docker-image-info';
        infoContainer.style.cssText = `
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            padding: 12px;
            margin-top: 8px;
            font-size: 12px;
            display: none;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            max-width: 500px;
        `;

        infoContainer.innerHTML = `
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                <h4 style="margin: 0; color: #495057; font-size: 14px;">📋 镜像信息</h4>
                <button id="toggleInfoBtn" style="background: none; border: none; cursor: pointer; color: #6c757d; font-size: 12px;">▼</button>
            </div>
            <div id="infoContent" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;">
                <div class="info-item">
                    <div class="info-label" style="color: #6c757d; font-weight: 600; margin-bottom: 2px;">镜像名称</div>
                    <div class="info-value" id="displayImageName" style="color: #495057; font-family: monospace; font-size: 11px;">-</div>
                </div>
                <div class="info-item">
                    <div class="info-label" style="color: #6c757d; font-weight: 600; margin-bottom: 2px;">当前架构</div>
                    <div class="info-value" id="displayArchitecture" style="color: #495057; font-family: monospace;">-</div>
                </div>
                <div class="info-item">
                    <div class="info-label" style="color: #6c757d; font-weight: 600; margin-bottom: 2px;">总大小</div>
                    <div class="info-value" id="displayTotalSize" style="color: #495057;">-</div>
                </div>
                <div class="info-item">
                    <div class="info-label" style="color: #6c757d; font-weight: 600; margin-bottom: 2px;">层数量</div>
                    <div class="info-value" id="displayLayerCount" style="color: #495057;">-</div>
                </div>
                <div class="info-item" style="grid-column: span 2;">
                    <div class="info-label" style="color: #6c757d; font-weight: 600; margin-bottom: 2px;">下载进度</div>
                    <div id="displayProgress" style="color: #495057;">
                        <div style="background: #e9ecef; border-radius: 10px; height: 6px; margin: 4px 0;">
                            <div id="progressBar" style="background: #007bff; height: 100%; border-radius: 10px; width: 0%; transition: width 0.3s ease;"></div>
                        </div>
                        <div id="progressText" style="font-size: 11px; color: #6c757d;">准备中...</div>
                    </div>
                </div>
                <div class="info-item" style="grid-column: span 2;">
                    <div class="info-label" style="color: #6c757d; font-weight: 600; margin-bottom: 2px;">可用架构</div>
                    <div id="displayAvailableArchs" style="color: #495057; font-size: 11px;">检测中...</div>
                </div>
            </div>
        `;

        // 添加折叠/展开功能
        const toggleBtn = infoContainer.querySelector('#toggleInfoBtn');
        const content = infoContainer.querySelector('#infoContent');
        
        toggleBtn.addEventListener('click', () => {
            if (content.style.display === 'none') {
                content.style.display = 'grid';
                toggleBtn.textContent = '▼';
            } else {
                content.style.display = 'none';
                toggleBtn.textContent = '▶';
            }
        });

        return infoContainer;
    }

    /**
     * 更新镜像信息展示
     * 功能:更新信息展示区域的各项数据
     * @param {Object} info - 镜像信息对象
     */
    function updateImageInfoDisplay(info) {
        const infoContainer = document.getElementById('dockerImageInfo');
        if (!infoContainer) return;

        // 显示信息容器
        infoContainer.style.display = 'block';

        // 更新各项信息
        if (info.imageName) {
            document.getElementById('displayImageName').textContent = info.imageName;
        }
        if (info.architecture) {
            document.getElementById('displayArchitecture').textContent = info.architecture;
        }
        if (info.totalSize) {
            document.getElementById('displayTotalSize').textContent = formatSize(info.totalSize);
        }
        if (info.layerCount !== undefined) {
            document.getElementById('displayLayerCount').textContent = info.layerCount;
        }
        if (info.availableArchs) {
            const archsElement = document.getElementById('displayAvailableArchs');
            if (info.availableArchs.length > 0) {
                archsElement.innerHTML = info.availableArchs.map(arch => 
                    `<span style="background: #e3f2fd; padding: 2px 6px; border-radius: 3px; margin: 1px; display: inline-block; font-family: monospace;">${arch}</span>`
                ).join('');
            } else {
                archsElement.textContent = '获取中...';
            }
        }
    }

    /**
     * 更新下载进度展示
     * 功能:更新进度条和进度文本
     * @param {number} percent - 进度百分比
     * @param {number} speed - 下载速度(字节/秒)
     */
    function updateProgressDisplay(percent, speed = 0) {
        const progressBar = document.getElementById('progressBar');
        const progressText = document.getElementById('progressText');
        
        if (progressBar && progressText) {
            progressBar.style.width = `${percent}%`;
            
            let statusText = '';
            if (percent === 0) {
                statusText = '准备中...';
                progressBar.style.background = '#6c757d';
            } else if (percent < 100) {
                statusText = `下载中 ${percent}%`;
                if (speed > 0) {
                    statusText += ` (${formatSpeed(speed)})`;
                }
                progressBar.style.background = '#007bff';
            } else {
                statusText = '下载完成';
                progressBar.style.background = '#28a745';
            }
            
            progressText.textContent = statusText;
        }
    }

    // ==================== 镜像分析功能 ====================

    /**
     * 分析镜像信息,获取清单数据
     * 功能:调用后端API获取镜像的层级结构和元数据
     * @param {string} imageName - 镜像名称
     * @param {string} imageTag - 镜像标签
     * @param {string} architecture - 目标架构(可选)
     * @returns {Promise<Object>} 镜像清单数据的Promise
     */
    async function analyzeImage(imageName, imageTag, architecture = '') {
        addLog(`开始分析镜像: ${imageName}:${imageTag}`);
        
        // 构建API请求URL
        let apiUrl = `${API_BASE_URL}/manifest?image=${encodeURIComponent(imageName)}&tag=${encodeURIComponent(imageTag)}`;
        if (architecture) {
            apiUrl += `&architecture=${encodeURIComponent(architecture)}`;
            addLog(`指定架构: ${architecture}`);
        }

        try {
            const response = await fetch(apiUrl);
            
            if (!response.ok) {
                throw new Error(`获取镜像信息失败: ${response.status}`);
            }

            const data = await response.json();
            addLog(`镜像分析完成,共 ${data.layerCount} 层,总大小 ${formatSize(data.totalSize)}`);
            
            return data;
        } catch (error) {
            addLog(`分析失败: ${error.message}`, 'error');
            throw error;
        }
    }

    /**
     * 检测镜像支持的架构
     * 功能:获取镜像的所有可用架构平台信息
     * @param {string} imageName - 镜像名称  
     * @param {string} imageTag - 镜像标签
     * @returns {Promise<Array>} 可用架构列表的Promise
     */
    async function detectArchitectures(imageName, imageTag) {
        addLog(`开始检测镜像架构: ${imageName}:${imageTag}`);
        
        try {
            const response = await fetch(`${API_BASE_URL}/manifest?image=${encodeURIComponent(imageName)}&tag=${encodeURIComponent(imageTag)}`);
            
            if (!response.ok) {
                throw new Error(`获取镜像信息失败: ${response.status}`);
            }

            const data = await response.json();
            
            if (data.multiArch && data.availablePlatforms) {
                addLog(`检测完成,发现 ${data.availablePlatforms.length} 种架构`);
                return data.availablePlatforms;
            } else {
                addLog('当前镜像仅支持单一架构');
                return [];
            }
        } catch (error) {
            addLog(`架构检测失败: ${error.message}`, 'error');
            throw error;
        }
    }

    // ==================== 下载功能 ====================

    /**
     * 下载单个镜像层(支持实时进度更新)
     * 功能:从API下载指定的镜像层数据并存储到本地缓存,支持实时进度反馈
     * @param {Object} layer - 层信息对象,包含digest、type、size等
     * @param {string} fullImageName - 完整的镜像名称
     * @returns {Promise} 下载完成的Promise
     */
    async function downloadLayer(layer, fullImageName) {
        const layerIndex = manifestData.layers.indexOf(layer);
        
        try {
            addLog(`开始下载层: ${layer.digest.substring(0, 12)}... (类型: ${layer.type}, 大小: ${formatSize(layer.size || 0)}, 索引: ${layerIndex})`);

            // 初始化进度跟踪
            downloadProgressMap.set(layer.digest, {
                downloaded: 0,
                total: layer.size || 0,
                speed: 0,
                completed: false,
                startTime: Date.now()
            });

            // 根据层类型构建不同的API端点URL
            let apiEndpoint;
            if (layer.type === 'config') {
                // 配置层的下载端点
                apiEndpoint = `${API_BASE_URL}/config?image=${encodeURIComponent(fullImageName)}&digest=${encodeURIComponent(layer.digest)}`;
            } else {
                // 普通镜像层的下载端点
                apiEndpoint = `${API_BASE_URL}/layer?image=${encodeURIComponent(fullImageName)}&digest=${encodeURIComponent(layer.digest)}`;
            }

            // 发起HTTP下载请求
            const response = await fetch(apiEndpoint);
            
            if (!response.ok) {
                throw new Error(`下载层失败: ${response.status}`);
            }

            // 使用流式读取来支持实时进度更新
            const reader = response.body.getReader();
            const chunks = [];
            let receivedLength = 0;
            const progressInfo = downloadProgressMap.get(layer.digest);
            
            while (true) {
                const { done, value } = await reader.read();
                
                if (done) break;
                
                chunks.push(value);
                receivedLength += value.length;
                
                // 更新进度信息
                const now = Date.now();
                const elapsed = (now - progressInfo.startTime) / 1000; // 秒
                progressInfo.downloaded = receivedLength;
                progressInfo.speed = elapsed > 0 ? receivedLength / elapsed : 0;
                
                downloadProgressMap.set(layer.digest, progressInfo);
            }

            // 组装完整的数据
            const arrayBuffer = new Uint8Array(receivedLength);
            let position = 0;
            for (const chunk of chunks) {
                arrayBuffer.set(chunk, position);
                position += chunk.length;
            }
            
            // 存储层数据
            if (useTemporaryCache) {
                // 使用临时缓存(minimal模式)
                tempLayerCache.set(layer.digest, arrayBuffer.buffer);
                addLog(`层数据存储到临时缓存: ${layer.digest.substring(0, 12)}... (${formatSize(arrayBuffer.byteLength)})`);
            } else {
                // 使用IndexedDB存储
                await storeLayerData(layer.digest, arrayBuffer.buffer);
                addLog(`层数据存储到IndexedDB: ${layer.digest.substring(0, 12)}... (${formatSize(arrayBuffer.byteLength)})`);
            }
            
            downloadedLayers.set(layer.digest, true);

            // 标记为完成
            progressInfo.completed = true;
            downloadProgressMap.set(layer.digest, progressInfo);

            addLog(`层下载完成: ${layer.digest.substring(0, 12)}... (${formatSize(arrayBuffer.byteLength)})`);
            
        } catch (error) {
            addLog(`层下载失败: ${layer.digest.substring(0, 12)}... - ${error.message}`, 'error');
            // 清理进度信息
            downloadProgressMap.delete(layer.digest);
            throw error;
        }
    }

    /**
     * 存储层数据到IndexedDB
     * 功能:将下载的层数据存储到IndexedDB数据库
     * @param {string} digest - 层摘要
     * @param {ArrayBuffer} data - 层数据
     * @returns {Promise} 存储完成的Promise
     */
    async function storeLayerData(digest, data) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction(['layers'], 'readwrite');
            const store = transaction.objectStore('layers');
            
            const layerRecord = {
                digest: digest,
                data: data,
                timestamp: Date.now()
            };
            
            const request = store.put(layerRecord);
            request.onsuccess = () => resolve();
            request.onerror = () => reject(request.error);
        });
    }

    /**
     * 从存储中获取层数据
     * 功能:优先从临时缓存获取,然后从IndexedDB获取
     * @param {string} digest - 层摘要
     * @returns {Promise<ArrayBuffer>} 层数据
     */
    async function getLayerData(digest) {
        // 首先检查临时缓存
        if (tempLayerCache.has(digest)) {
            return tempLayerCache.get(digest);
        }
        
        // 然后检查IndexedDB
        return new Promise((resolve, reject) => {
            const transaction = db.transaction(['layers'], 'readonly');
            const store = transaction.objectStore('layers');
            
            const request = store.get(digest);
            request.onsuccess = () => {
                if (request.result) {
                    resolve(request.result.data);
                } else {
                    reject(new Error('未找到层数据: ' + digest));
                }
            };
            request.onerror = () => reject(request.error);
        });
    }

    // ==================== 文件生成功能 ====================

    /**
     * 生成下载文件名
     * 功能:根据镜像名称、标签和架构生成规范的文件名
     * @param {string} imageName - 镜像名称
     * @param {string} imageTag - 镜像标签
     * @param {string} architecture - 架构信息
     * @returns {string} 生成的TAR文件名
     */
    function generateFilename(imageName, imageTag, architecture) {
        // 处理镜像名称,移除Docker Hub的library前缀
        let cleanImageName = imageName;
        if (cleanImageName.startsWith('library/')) {
            cleanImageName = cleanImageName.substring(8);
        }
        
        // 替换文件名中的特殊字符为安全字符
        cleanImageName = cleanImageName.replace(/[\/\\:]/g, '_').replace(/[<>:"|?*]/g, '-');
        const cleanTag = imageTag.replace(/[\/\\:]/g, '_').replace(/[<>:"|?*]/g, '-');
        const cleanArch = architecture.replace(/[\/\\:]/g, '_').replace(/[<>:"|?*]/g, '-') || 'amd64';
        
        // 返回格式:imagename_tag_architecture.tar
        return `${cleanImageName}_${cleanTag}_${cleanArch}.tar`;
    }

    /**
     * 生成随机摘要值
     * 功能:当镜像配置不可用时生成伪随机SHA256摘要
     * @returns {string} 64位十六进制摘要字符串
     */
    function generateFakeDigest() {
        const chars = '0123456789abcdef';
        let result = '';
        for (let i = 0; i < 64; i++) {
            result += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return result;
    }

    /**
     * 使用tarballjs创建Docker TAR文件
     * 功能:将所有下载的层组装成符合Docker标准的TAR格式文件
     * @param {Map} layerDataMap - 层数据映射表
     * @param {string} filename - 输出文件名
     * @returns {Promise} 创建完成的Promise
     */
    async function createDockerTar(layerDataMap, filename) {
        addLog('开始创建Docker TAR文件...');
        
        // 检查tarballjs库是否可用
        if (!window.tarball || !window.tarball.TarWriter) {
            throw new Error('tarballjs库未加载,无法创建TAR文件');
        }

        try {
            const tar = new tarball.TarWriter();

            // 第一步:处理镜像配置文件
            const manifest = manifestData.manifest;
            let configDigest = null;
            let configData = null;

            // 尝试从manifest中获取配置摘要
            if (manifest.config && manifest.config.digest) {
                configDigest = manifest.config.digest;
                const rawConfigData = layerDataMap.get(configDigest);
                if (rawConfigData) {
                    configData = new Uint8Array(rawConfigData);
                    addLog(`配置数据准备完成,大小: ${configData.length} 字节`);
                }
            }

            // 如果没有配置数据,创建默认配置
            if (!configData) {
                configDigest = 'sha256:' + generateFakeDigest();
                const configObj = {
                    architecture: "amd64",
                    os: "linux", 
                    config: {},
                    rootfs: {
                        type: "layers",
                        diff_ids: manifestData.layers
                            .filter(l => l.type === 'layer')
                            .map(l => l.digest)
                    }
                };
                configData = new TextEncoder().encode(JSON.stringify(configObj));
                addLog(`生成默认配置,大小: ${configData.length} 字节`);
            }

            // 第二步:添加配置文件到TAR
            const configFileName = configDigest + '.json';
            const configBlob = new Blob([configData], { type: 'application/json' });
            const configFile = new File([configBlob], configFileName);
            tar.addFile(configFileName, configFile);
            addLog(`添加配置文件: ${configFileName}`);

            // 第三步:添加所有镜像层到TAR
            const layerDigests = [];
            let layerIndex = 0;
            for (const layer of manifestData.layers) {
                if (layer.type === 'layer' && layer.digest) {
                    const layerDigest = layer.digest;
                    layerDigests.push(layerDigest);

                    const layerData = layerDataMap.has(layerDigest) ? layerDataMap.get(layerDigest) : await getLayerData(layerDigest);
                    if (layerData) {
                        // 每个层创建独立目录结构: digest/layer.tar
                        const layerFileName = layerDigest + '/layer.tar';
                        const layerUint8Array = new Uint8Array(layerData);
                        const layerBlob = new Blob([layerUint8Array], { type: 'application/octet-stream' });
                        const layerFile = new File([layerBlob], 'layer.tar');

                        tar.addFile(layerFileName, layerFile);
                        addLog(`添加层文件 ${layerIndex + 1}/${manifestData.layers.filter(l => l.type === 'layer').length}: ${layerFileName}`);
                        layerIndex++;
                    }
                }
            }

            // 第四步:创建Docker manifest.json文件
            let repoTag = manifestData.imageName;
            if (repoTag.startsWith('library/')) {
                repoTag = repoTag.substring(8);
            }
            if (!repoTag.includes(':')) {
                repoTag += ':latest';
            }

            const dockerManifest = [{
                Config: configFileName,
                RepoTags: [repoTag],
                Layers: layerDigests.map(digest => digest + '/layer.tar')
            }];

            const manifestBlob = new Blob([JSON.stringify(dockerManifest)], { type: 'application/json' });
            const manifestFile = new File([manifestBlob], 'manifest.json');
            tar.addFile('manifest.json', manifestFile);
            addLog('添加manifest.json文件');

            // 第五步:创建repositories文件
            const repositories = {};
            let repoName, tag;
            if (manifestData.imageName.includes(':')) {
                const parts = manifestData.imageName.split(':');
                repoName = parts[0];
                tag = parts[1];
            } else {
                repoName = manifestData.imageName;
                tag = 'latest';
            }

            if (repoName.startsWith('library/')) {
                repoName = repoName.substring(8);
            }

            repositories[repoName] = {};
            repositories[repoName][tag] = configDigest.replace('sha256:', '');

            const repositoriesBlob = new Blob([JSON.stringify(repositories)], { type: 'application/json' });
            const repositoriesFile = new File([repositoriesBlob], 'repositories');
            tar.addFile('repositories', repositoriesFile);
            addLog('添加repositories文件');

            // 第六步:下载生成的TAR文件
            addLog('开始生成并下载TAR文件...');
            tar.download(filename);
            addLog(`TAR文件下载已触发: ${filename}`);

        } catch (error) {
            addLog(`创建TAR文件失败: ${error.message}`, 'error');
            throw error;
        }
    }

    // ==================== 完整下载流程 ====================

    /**
     * 执行完整的镜像下载流程
     * 功能:包括分析、下载、组装的完整自动化流程
     * @param {string} imageName - 镜像名称
     * @param {string} imageTag - 镜像标签  
     * @param {string} architecture - 目标架构
     */
    async function performDownload(imageName, imageTag, architecture) {
        // 防止重复下载
        if (downloadInProgress) {
            addLog('下载正在进行中,请等待当前下载完成', 'error');
            return;
        }

        const downloadBtn = document.getElementById('downloadBtn');
        const originalText = downloadBtn.textContent;

        try {
            // 设置下载状态
            downloadInProgress = true;

            // 清理之前的下载数据
            tempLayerCache.clear();
            downloadedLayers.clear();
            downloadProgressMap.clear();

            // 第一步:分析镜像
            addLog('=== 开始镜像下载流程 ===');
            addLog(`镜像: ${imageName}:${imageTag}`);
            if (architecture) {
                addLog(`架构: ${architecture}`);
            } else {
                addLog('架构: 自动检测');
            }
            
            updateButtonProgress('🔍 分析镜像', 'analyzing', {
                size: 0
            });
            manifestData = await analyzeImage(imageName, imageTag, architecture);

            // 第二步:选择内存策略
            chooseMemoryStrategy();

            // 第三步:启动实时进度更新并开始下载
            updateButtonProgress('⬇️ 下载镜像层', 'downloading', {
                progress: 0,
                current: 0,
                total: manifestData.layers.length,
                size: manifestData.totalSize
            });
            addLog(`开始下载 ${manifestData.layers.length} 个镜像层`);

            // 启动实时进度更新
            startRealTimeProgressUpdate();

            const fullImageName = imageName;
            const downloadPromises = manifestData.layers.map(async (layer) => {
                await downloadLayer(layer, fullImageName);
            });

            await Promise.all(downloadPromises);
            
            // 停止实时进度更新
            stopRealTimeProgressUpdate();
            
            addLog('所有镜像层下载完成');

            // 第四步:组装Docker TAR文件
            updateButtonProgress('🔧 组装镜像', 'assembling', {
                progress: 100,
                current: manifestData.layers.length,
                total: manifestData.layers.length,
                size: manifestData.totalSize
            });
            addLog('开始组装Docker TAR文件');

            const filename = generateFilename(imageName, imageTag, architecture || 'amd64');
            
            // 根据存储模式传递不同的数据源
            if (useTemporaryCache) {
                await createDockerTar(tempLayerCache, filename);
            } else {
                // 对于IndexedDB模式,传递空Map,让createDockerTar使用getLayerData获取数据
                await createDockerTar(new Map(), filename);
            }

            addLog('=== 镜像下载流程完成 ===');
            updateButtonProgress('✅ 下载完成', 'complete', {
                size: manifestData.totalSize
            });

        } catch (error) {
            addLog(`下载失败: ${error.message}`, 'error');
            updateButtonProgress('❌ 下载失败', 'error');
            
            // 确保停止实时进度更新
            stopRealTimeProgressUpdate();
        } finally {
            downloadInProgress = false;
            
            // 清理进度数据
            downloadProgressMap.clear();
        }
    }

    // ==================== UI交互功能 ====================

    /**
     * 更新架构选择器选项
     * 功能:根据检测到的可用架构更新下拉选择器
     * @param {Array} platforms - 可用平台列表
     */
    function updateArchSelector(platforms) {
        const archSelector = document.getElementById('archSelector');
        if (!archSelector || !platforms || platforms.length === 0) return;

        // 清空现有选项
        archSelector.innerHTML = '';

        // 添加检测到的架构选项
        platforms.forEach(platform => {
            if (platform && platform.platform) {
                const option = document.createElement('option');
                option.value = platform.platform;
                option.textContent = platform.platform;
                archSelector.appendChild(option);
            }
        });

        // 智能选择首选架构
        // 优先级:linux/amd64 > linux/arm64 > 其他linux架构 > 第一个可用架构
        let selectedPlatform = platforms.find(p => p.platform === 'linux/amd64') ||
                              platforms.find(p => p.platform === 'linux/arm64') ||
                              platforms.find(p => p.os === 'linux') ||
                              platforms[0];

        if (selectedPlatform) {
            archSelector.value = selectedPlatform.platform;
            addLog(`自动选择架构: ${selectedPlatform.platform}`);
        }
    }

    /**
     * 绑定UI事件处理器
     * 功能:为下载按钮和架构检测按钮绑定点击事件
     */
    function bindEventHandlers() {
        // 主下载按钮点击事件
        const downloadBtn = document.getElementById('downloadBtn');
        if (downloadBtn) {
            downloadBtn.addEventListener('click', async () => {
                const { imageName, imageTag } = extractImageInfo();
                const archSelector = document.getElementById('archSelector');
                let architecture = archSelector ? archSelector.value : '';

                // 验证镜像信息是否提取成功
                if (!imageName) {
                    addLog('无法获取镜像名称,请确保在正确的Docker Hub页面', 'error');
                    alert('无法获取镜像名称!\n\n请确保您在正确的Docker Hub镜像页面:\n- 官方镜像:hub.docker.com/r/nginx\n- 用户镜像:hub.docker.com/r/username/imagename');
                    return;
                }

                // 如果没有手动选择架构或选择了"自动检测",则使用自动检测功能
                if (!architecture || architecture === '') {
                    addLog('未手动选择架构,启用自动检测...');
                    architecture = await autoDetectArchitecture();
                    
                    // 更新架构选择器显示检测结果
                    if (archSelector && architecture) {
                        const existingOption = archSelector.querySelector(`option[value="${architecture}"]`);
                        if (existingOption) {
                            archSelector.value = architecture;
                        } else {
                            // 添加检测到的架构选项
                            const newOption = document.createElement('option');
                            newOption.value = architecture;
                            newOption.textContent = architecture;
                            newOption.selected = true;
                            archSelector.appendChild(newOption);
                        }
                        addLog(`自动检测并设置架构: ${architecture}`);
                    }
                }

                // 开始下载流程
                await performDownload(imageName, imageTag, architecture);
            });
        } else {
            console.log('下载按钮未找到,稍后重试绑定事件');
        }
    }

    // ==================== 页面集成功能 ====================

    /**
     * 查找合适的位置插入下载按钮
     * 功能:简化版本,直接查找页面标题
     * @returns {Array} 包含插入点信息的数组
     */
    function findInsertionPoints() {
        const insertionPoints = [];

        // 如果已经存在下载器,不重复添加
        if (document.querySelector('[data-docker-downloader]')) {
            console.log('页面已存在下载器,跳过插入点查找');
            return insertionPoints;
        }

        // 方法1: 查找h1标题
        const title = document.querySelector('h1');
        if (title) {
            console.log('找到h1标题:', title.textContent.substring(0, 50));
            insertionPoints.push({
                type: 'title',
                element: title.parentElement || title,
                position: 'inside',
                description: 'H1标题区域'
            });
        }

        // 方法2: 查找其他可能的标题元素
        const altTitles = document.querySelectorAll('h2, h3, [data-testid*="title"], .repository-title, .repo-title');
        altTitles.forEach((altTitle, index) => {
            if (altTitle.textContent.trim()) {
                console.log(`找到备用标题${index + 1}:`, altTitle.textContent.substring(0, 50));
                insertionPoints.push({
                    type: 'alt-title',
                    element: altTitle.parentElement || altTitle,
                    position: 'inside',
                    description: `备用标题区域${index + 1}`
                });
            }
        });

        // 方法3: 查找页面主要内容区域
        const mainContent = document.querySelector('main, .main-content, .content, #content');
        if (mainContent && insertionPoints.length === 0) {
            console.log('找到主要内容区域');
            insertionPoints.push({
                type: 'main',
                element: mainContent,
                position: 'prepend',
                description: '主要内容区域顶部'
            });
        }

        // 方法4: 最后的备用方案 - body元素
        if (insertionPoints.length === 0) {
            console.log('使用body作为最后的插入点');
            insertionPoints.push({
                type: 'body',
                element: document.body,
                position: 'prepend',
                description: '页面顶部'
            });
        }

        console.log(`找到 ${insertionPoints.length} 个可能的插入点`);
        return insertionPoints;
    }

    /**
     * 在指定位置插入下载按钮
     * 功能:根据插入点类型和位置插入相应的按钮
     * @param {Object} insertionPoint - 插入点信息对象
     */
    function insertDownloadButtons(insertionPoint) {
        const { element, position, type } = insertionPoint;
        
        if (!element) {
            console.error('插入点元素不存在');
            return;
        }

        // 创建按钮容器
        const buttonContainer = document.createElement('span');
        buttonContainer.className = 'docker-download-container';
        buttonContainer.setAttribute('data-docker-downloader', 'true');
        buttonContainer.style.cssText = `
            display: inline-flex;
            align-items: center;
            gap: 8px;
            margin: 0 10px;
            vertical-align: middle;
        `;

        // 创建下载按钮
        const downloadBtn = createInlineDownloadButton();
        
        // 创建架构选择器(紧凑型)
        const archSelector = createArchSelector();
        archSelector.style.fontSize = '11px';
        archSelector.style.padding = '4px 8px';
        archSelector.style.minWidth = '100px';

        // 创建架构检测状态指示器
        const archIndicator = document.createElement('span');
        archIndicator.id = 'archIndicator';
        archIndicator.style.cssText = `
            font-size: 11px;
            color: #666;
            margin-left: 5px;
            display: none;
        `;
        archIndicator.textContent = '🔍 检测中...';

        // 将元素添加到容器
        buttonContainer.appendChild(downloadBtn);
        buttonContainer.appendChild(archSelector);
        buttonContainer.appendChild(archIndicator);

        // 根据位置类型插入按钮
        switch (position) {
            case 'after':
                // 在元素后面插入
                if (element.nextSibling) {
                    element.parentNode.insertBefore(buttonContainer, element.nextSibling);
                } else {
                    element.parentNode.appendChild(buttonContainer);
                }
                break;
                
            case 'before':
                // 在元素前面插入
                element.parentNode.insertBefore(buttonContainer, element);
                break;
                
            case 'inside':
                // 在元素内部插入
                element.appendChild(buttonContainer);
                break;
                
            case 'prepend':
                // 在元素内部最前面插入
                element.insertBefore(buttonContainer, element.firstChild);
                break;
                
            default:
                // 默认在元素后面插入
                element.parentNode.appendChild(buttonContainer);
        }

        // 移除复杂的悬停逻辑,保持架构选择器始终可见
        // 进度信息直接显示在按钮上,不需要额外的进度区域

        console.log(`下载按钮已插入到: ${insertionPoint.description}`);
    }

    /**
     * 检查是否在Docker Hub镜像页面
     * 功能:验证当前页面是否适合显示下载器
     * @returns {boolean} 是否在合适的镜像页面
     */
    function isDockerHubImagePage() {
        const url = window.location.href;
        const pathname = window.location.pathname;
        
        // 首先检查是否在Docker Hub域名
        if (!url.includes('hub.docker.com')) {
            console.log('不在Docker Hub域名');
            return false;
        }

        // 排除首页和其他非镜像页面
        const excludePatterns = [
            /^\/$/,                                 // 首页
            /^\/search/,                           // 搜索页面
            /^\/explore/,                          // 探索页面
            /^\/extensions/,                       // 扩展页面
            /^\/pricing/,                          // 价格页面
            /^\/signup/,                           // 注册页面
            /^\/login/,                            // 登录页面
            /^\/u\//,                              // 用户页面
            /^\/orgs\//,                           // 组织页面
            /^\/repositories/,                     // 仓库列表页面
            /^\/settings/,                         // 设置页面
            /^\/billing/,                          // 账单页面
            /^\/support/                           // 支持页面
        ];

        // 如果匹配排除模式,直接返回false
        const isExcludedPage = excludePatterns.some(pattern => pattern.test(pathname));
        if (isExcludedPage) {
            console.log('页面被排除:', pathname);
            return false;
        }

        // 检查是否在镜像相关页面(严格检测)
        const imagePagePatterns = [
            /^\/r\/[^\/]+$/,                        // /r/nginx (官方镜像)
            /^\/r\/[^\/]+\/[^\/]+$/,                // /r/username/imagename (用户镜像)
            /^\/_\/[^\/]+$/,                        // /_/nginx (官方镜像另一格式)
            /^\/layers\/[^\/]+\/[^\/]+\/[^\/]+/,    // /layers/username/imagename/tag/... (镜像层详情页面)
            /^\/r\/[^\/]+\/tags/,                   // 官方镜像标签页面
            /^\/r\/[^\/]+\/[^\/]+\/tags/            // 用户镜像标签页面
        ];

        const isImagePage = imagePagePatterns.some(pattern => pattern.test(pathname));
        console.log('页面路径检测:', pathname, '是镜像页面:', isImagePage);
        
        return isImagePage;
    }

    // ==================== 初始化和启动 ====================

    /**
     * 清理旧的下载器界面
     * 功能:删除页面上任何已存在的下载器界面
     */
    function cleanupOldDownloaders() {
        // 删除旧的大型下载器界面
        const oldDownloaders = document.querySelectorAll('.docker-downloader-container');
        oldDownloaders.forEach(element => {
            if (element.querySelector('.docker-downloader-title')) {
                element.remove();
                console.log('已删除旧的下载器界面');
            }
        });

        // 删除重复的按钮容器
        const containers = document.querySelectorAll('.docker-download-container');
        if (containers.length > 1) {
            // 保留第一个,删除其余的
            for (let i = 1; i < containers.length; i++) {
                containers[i].remove();
                console.log('已删除重复的下载按钮');
            }
        }
    }

    /**
     * 初始化下载器主函数
     * 功能:执行所有必要的初始化步骤并启动下载器
     */
    async function initDownloader() {
        try {
            console.log('开始初始化Docker下载器...');
            console.log('当前URL:', window.location.href);
            console.log('当前路径:', window.location.pathname);
            
            // 等待页面DOM加载完成
            if (document.readyState === 'loading') {
                console.log('等待DOM加载完成...');
                await new Promise(resolve => {
                    document.addEventListener('DOMContentLoaded', resolve);
                });
            }

            // 检查是否在合适的Docker Hub页面
            const isImagePage = isDockerHubImagePage();
            console.log('是否为镜像页面:', isImagePage);
            if (!isImagePage) {
                console.log('不在Docker Hub镜像页面,跳过下载器初始化');
                return;
            }

            // 清理旧的下载器界面
            console.log('清理旧的下载器界面...');
            cleanupOldDownloaders();

            // 避免重复初始化 - 检查是否还有按钮存在
            const existingBtn = document.querySelector('.docker-download-btn');
            if (existingBtn) {
                console.log('下载按钮仍然存在,跳过初始化');
                return;
            }

            // 添加CSS样式
            addCustomStyles();

            // 初始化本地数据库
            await initIndexedDB();

            // 等待页面元素完全加载
            await new Promise(resolve => setTimeout(resolve, 1500));

            // 查找插入点并插入下载按钮
            console.log('开始查找插入点...');
            const insertionPoints = findInsertionPoints();
            console.log('找到插入点数量:', insertionPoints.length);
            
            if (insertionPoints.length > 0) {
                // 选择最佳插入点
                let bestPoint = insertionPoints[0]; // 使用找到的第一个有效点
                console.log('选择插入点:', bestPoint.description, bestPoint);
                insertDownloadButtons(bestPoint);
                
                // 在按钮插入后绑定事件处理器
                setTimeout(() => {
                    bindEventHandlers();
                    // 设置架构变化监听器
                    setupArchChangeListener();
                }, 100);
            } else {
                console.log('未找到插入点,这不应该发生,因为findInsertionPoints已经有备用方案');
                
                // 强制备用方案:直接在body中插入
                const forcePoint = {
                    element: document.body,
                    position: 'prepend',
                    type: 'force',
                    description: '强制插入到页面顶部'
                };
                console.log('使用强制插入点:', forcePoint.description);
                insertDownloadButtons(forcePoint);
                
                // 在按钮插入后绑定事件处理器
                setTimeout(() => {
                    bindEventHandlers();
                    // 设置架构变化监听器
                    setupArchChangeListener();
                }, 100);
            }

            // 记录初始化完成
            addLog('Docker Hub 下载按钮已准备就绪');
            
            // 显示当前检测到的镜像信息
            const { imageName, imageTag } = extractImageInfo();
            if (imageName) {
                addLog(`检测到镜像: ${imageName}:${imageTag}`);
            } else {
                addLog('等待镜像信息加载...');
            }

        } catch (error) {
            console.error('初始化下载器失败:', error);
            // 即使初始化失败也不要影响页面正常使用
        }
    }

    /**
     * 设置页面变化监听器
     * 功能:监听单页应用的路由变化并重新初始化
     */
    function setupPageChangeListener() {
        let currentUrl = window.location.href;
        let initTimeout = null;
        
        // 防抖函数,避免频繁初始化
        function debouncedInit() {
            if (initTimeout) {
                clearTimeout(initTimeout);
            }
            initTimeout = setTimeout(() => {
                console.log('页面变化检测到,重新初始化下载器');
                initDownloader();
            }, 1000); // 减少延迟时间
        }
        
        // 更敏感的URL变化检测
        function checkUrlChange() {
            if (window.location.href !== currentUrl) {
                console.log('URL变化检测:', currentUrl, '->', window.location.href);
                currentUrl = window.location.href;
                debouncedInit();
            }
        }
        
        // 使用MutationObserver监听DOM变化
        let lastCheck = 0;
        const observer = new MutationObserver((mutations) => {
            const now = Date.now();
            if (now - lastCheck > 1000) { // 减少检查间隔
                lastCheck = now;
                checkUrlChange();
                
                // 检查是否有新的页面内容加载
                for (const mutation of mutations) {
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                        for (const node of mutation.addedNodes) {
                            if (node.nodeType === Node.ELEMENT_NODE) {
                                // 检查是否包含镜像相关内容
                                const text = node.textContent || '';
                                if (text.includes('MANIFEST DIGEST') || 
                                    text.includes('OS/ARCH') || 
                                    text.includes('Image Layers') ||
                                    node.querySelector && node.querySelector('[data-testid]')) {
                                    console.log('检测到镜像页面内容加载');
                                    debouncedInit();
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        });

        // 开始观察页面内容变化
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: false,
            characterData: false
        });

        // 监听浏览器前进后退按钮
        window.addEventListener('popstate', () => {
            console.log('浏览器导航事件');
            debouncedInit();
        });
        
        // 监听pushState和replaceState(SPA路由变化)
        const originalPushState = history.pushState;
        const originalReplaceState = history.replaceState;
        
        history.pushState = function() {
            originalPushState.apply(history, arguments);
            console.log('pushState事件');
            setTimeout(checkUrlChange, 100);
        };
        
        history.replaceState = function() {
            originalReplaceState.apply(history, arguments);
            console.log('replaceState事件');
            setTimeout(checkUrlChange, 100);
        };
        
        // 定期检查URL变化(备用方案)
        setInterval(checkUrlChange, 2000);
        
        console.log('页面变化监听器已设置');
    }

    // ==================== 架构自动检测功能 ====================

    /**
     * 从页面DOM自动检测当前选中的架构
     * 功能:从Docker Hub页面的架构选择器中提取当前选中的架构信息
     * @returns {string} 检测到的架构字符串,如 'linux/arm64'
     */
    function detectArchFromPageDOM() {
        try {
            // 方法1: 优先检测OS/ARCH部分的架构选择器(基于您的截图)
            // 查找包含"OS/ARCH"文本的区域附近的选择器
            const osArchHeaders = document.querySelectorAll('*');
            for (const header of osArchHeaders) {
                const headerText = header.textContent.trim();
                if (headerText === 'OS/ARCH' || headerText.includes('OS/ARCH')) {
                    // 在OS/ARCH标题附近查找选择器
                    const parent = header.parentElement;
                    if (parent) {
                        // 查找父元素及其兄弟元素中的选择器
                        const nearbySelectors = parent.querySelectorAll('select, [role="combobox"], .MuiSelect-select');
                        for (const selector of nearbySelectors) {
                            let archText = '';
                            
                            if (selector.tagName === 'SELECT') {
                                const selectedOption = selector.options[selector.selectedIndex];
                                archText = selectedOption ? selectedOption.textContent.trim() : selector.value;
                            } else {
                                archText = selector.textContent.trim();
                            }
                            
                            if (archText.match(/^(linux|windows|darwin)\/.+/i)) {
                                addLog(`从OS/ARCH区域检测到架构: ${archText}`);
                                return archText.toLowerCase();
                            }
                        }
                        
                        // 也检查父元素的下一个兄弟元素
                        let nextElement = parent.nextElementSibling;
                        while (nextElement) {
                            const selectors = nextElement.querySelectorAll('select, [role="combobox"], .MuiSelect-select');
                            for (const selector of selectors) {
                                let archText = '';
                                
                                if (selector.tagName === 'SELECT') {
                                    const selectedOption = selector.options[selector.selectedIndex];
                                    archText = selectedOption ? selectedOption.textContent.trim() : selector.value;
                                } else {
                                    archText = selector.textContent.trim();
                                }
                                
                                if (archText.match(/^(linux|windows|darwin)\/.+/i)) {
                                    addLog(`从OS/ARCH区域下方检测到架构: ${archText}`);
                                    return archText.toLowerCase();
                                }
                            }
                            nextElement = nextElement.nextElementSibling;
                            // 只检查接下来的几个兄弟元素,避免检查过远
                            if (nextElement && nextElement.getBoundingClientRect().top - parent.getBoundingClientRect().top > 200) {
                                break;
                            }
                        }
                    }
                }
            }

            // 方法2: 检测标准的架构下拉选择器
            const osArchSelectors = [
                'select', // 标准select元素
                'select[aria-label*="arch"]', // 带有arch标签的select
                'select[aria-label*="OS"]', // 带有OS标签的select
                '.MuiSelect-select', // MUI选择器
                '[role="combobox"]' // 下拉框角色
            ];

            // 收集所有可能的架构选择器及其文本
            const archCandidates = [];
            
            for (const selector of osArchSelectors) {
                const elements = document.querySelectorAll(selector);
                for (const element of elements) {
                    let archText = '';
                    let elementInfo = '';
                    
                    // 检查select元素的选中值
                    if (element.tagName === 'SELECT') {
                        const selectedValue = element.value;
                        const selectedOption = element.options[element.selectedIndex];
                        archText = selectedOption ? selectedOption.textContent.trim() : selectedValue;
                        elementInfo = `SELECT(${element.className})`;
                    } else {
                        // 检查其他元素的文本内容
                        archText = element.textContent.trim();
                        elementInfo = `${element.tagName}(${element.className})`;
                    }
                    
                    if (archText.match(/^(linux|windows|darwin)\/.+/i)) {
                        archCandidates.push({
                            text: archText.toLowerCase(),
                            element: element,
                            info: elementInfo,
                            position: element.getBoundingClientRect()
                        });
                    }
                }
            }

            // 如果找到多个候选者,选择最合适的一个
            if (archCandidates.length > 0) {
                addLog(`找到 ${archCandidates.length} 个架构候选:`);
                archCandidates.forEach((candidate, index) => {
                    addLog(`  候选${index + 1}: ${candidate.text} (${candidate.info}) 位置: ${Math.round(candidate.position.top)}`);
                });
                
                // 优先选择位置较低的(通常OS/ARCH部分在页面下方)
                archCandidates.sort((a, b) => b.position.top - a.position.top);
                const selected = archCandidates[0];
                addLog(`选择架构: ${selected.text} (${selected.info}) - 位置最低`);
                return selected.text;
            }

            // 方法2: 检测MUI架构选择器(基于您提供的DOM结构)
            const muiSelectors = [
                '.MuiSelect-select.MuiSelect-outlined.MuiInputBase-input.MuiOutlinedInput-input.MuiInputBase-inputSizeSmall', // 完整MUI选择器
                '.MuiSelect-select.MuiSelect-outlined', // MUI下拉选择器
                '.MuiInputBase-input.MuiOutlinedInput-input', // MUI输入框
                'div[role="combobox"]' // div形式的下拉框
            ];

            for (const selector of muiSelectors) {
                const elements = document.querySelectorAll(selector);
                for (const element of elements) {
                    const text = element.textContent.trim();
                    // 检查是否包含架构格式 (os/arch)
                    if (text.match(/^(linux|windows|darwin)\/.+/i)) {
                        addLog(`从MUI选择器检测到架构: ${text}`);
                        return text.toLowerCase();
                    }
                }
            }

            // 方法3: 专门检测您提供的MUI容器结构
            const muiContainers = document.querySelectorAll('.MuiInputBase-root.MuiOutlinedInput-root');
            for (const container of muiContainers) {
                const selectDiv = container.querySelector('div[role="combobox"]');
                if (selectDiv) {
                    const text = selectDiv.textContent.trim();
                    if (text.match(/^(linux|windows|darwin)\/.+/i)) {
                        addLog(`从MUI容器检测到架构: ${text}`);
                        return text.toLowerCase();
                    }
                }
            }

            // 方法2: 查找包含架构信息的其他元素
            const archPatterns = [
                /linux\/amd64/i,
                /linux\/arm64/i,
                /linux\/arm\/v7/i,
                /linux\/arm\/v6/i,
                /linux\/386/i,
                /windows\/amd64/i,
                /darwin\/amd64/i,
                /darwin\/arm64/i
            ];

            // 搜索页面中所有可能包含架构信息的元素
            const allElements = document.querySelectorAll('*');
            for (const element of allElements) {
                const text = element.textContent.trim();
                for (const pattern of archPatterns) {
                    if (pattern.test(text) && text.length < 50) { // 避免匹配过长的文本
                        const match = text.match(pattern);
                        if (match) {
                            addLog(`从页面元素检测到架构: ${match[0]}`);
                            return match[0].toLowerCase();
                        }
                    }
                }
            }

            addLog('未能从页面DOM检测到架构信息');
            return null;

        } catch (error) {
            addLog(`DOM架构检测失败: ${error.message}`, 'error');
            return null;
        }
    }

    /**
     * 从URL中的SHA256值检测架构(使用缓存信息)
     * 功能:使用缓存的架构-SHA256映射快速检测架构
     * @returns {Promise<string|null>} 检测到的架构字符串
     */
    async function detectArchFromSHA256() {
        try {
            const url = window.location.href;
            const sha256Match = url.match(/sha256-([a-f0-9]{64})/i);
            
            if (!sha256Match) {
                addLog('URL中未找到SHA256值');
                return null;
            }

            const sha256 = sha256Match[1];
            addLog(`从URL提取SHA256: ${sha256.substring(0, 12)}...`);

            // 确保有缓存的架构信息
            if (!cachedArchitectures) {
                addLog('没有缓存的架构信息,先获取架构列表');
                await getAvailableArchitectures();
            }

            if (!cachedArchitectures || !cachedArchitectures.platformMap) {
                addLog('无法获取架构信息进行SHA256检测');
                return null;
            }

            // 首先检查平台映射中是否有直接匹配的SHA256
            for (const [architecture, platformInfo] of cachedArchitectures.platformMap) {
                if (platformInfo.sha256 && platformInfo.sha256 === sha256) {
                    addLog(`✅ 通过缓存的平台信息匹配到架构: ${architecture}`);
                    return architecture;
                }
                if (platformInfo.digest && platformInfo.digest.includes(sha256)) {
                    addLog(`✅ 通过缓存的摘要信息匹配到架构: ${architecture}`);
                    return architecture;
                }
            }

            // 如果缓存中没有找到,需要深度检查每个架构的层信息
            addLog('缓存中未找到匹配,深度检查各架构的层信息...');
            
            const { imageName, imageTag } = extractImageInfo();
            if (!imageName) {
                addLog('无法提取镜像信息进行深度SHA256检测');
                return null;
            }

            for (const architecture of cachedArchitectures.architectures) {
                addLog(`  深度检查架构: ${architecture}`);
                
                try {
                    const archManifestUrl = `${API_BASE_URL}/manifest?image=${encodeURIComponent(imageName)}&tag=${encodeURIComponent(imageTag)}&architecture=${encodeURIComponent(architecture)}`;
                    const archResponse = await fetch(archManifestUrl);
                    
                    if (archResponse.ok) {
                        const archData = await archResponse.json();
                        
                        // 检查这个架构的所有层是否包含我们的SHA256
                        if (archData.layers) {
                            for (const layer of archData.layers) {
                                if (layer.digest && layer.digest.includes(sha256)) {
                                    addLog(`✅ SHA256匹配镜像层! 架构: ${architecture}`);
                                    
                                    // 更新缓存信息
                                    const platformInfo = cachedArchitectures.platformMap.get(architecture);
                                    if (platformInfo) {
                                        platformInfo.layerSha256 = sha256;
                                    }
                                    
                                    return architecture;
                                }
                            }
                        }
                        
                        // 也检查配置层
                        if (archData.config && archData.config.digest && archData.config.digest.includes(sha256)) {
                            addLog(`✅ SHA256匹配配置层! 架构: ${architecture}`);
                            
                            // 更新缓存信息
                            const platformInfo = cachedArchitectures.platformMap.get(architecture);
                            if (platformInfo) {
                                platformInfo.configSha256 = sha256;
                            }
                            
                            return architecture;
                        }
                    }
                } catch (archError) {
                    addLog(`  检查架构 ${architecture} 时出错: ${archError.message}`);
                }
            }

            addLog('SHA256架构检测未找到匹配结果');
            return null;

        } catch (error) {
            addLog(`SHA256架构检测失败: ${error.message}`, 'error');
            return null;
        }
    }

    /**
     * 综合架构自动检测
     * 功能:结合多种方法自动检测当前页面的架构信息
     * @returns {Promise<string|null>} 检测到的架构字符串
     */
    async function autoDetectArchitecture() {
        addLog('开始自动架构检测...');

        // 优先级1: 从URL的SHA256检测(最准确)
        const sha256Arch = await detectArchFromSHA256();
        if (sha256Arch) {
            return sha256Arch;
        }

        // 优先级2: 从页面DOM检测
        const domArch = detectArchFromPageDOM();
        if (domArch) {
            return domArch;
        }

        // 优先级3: 使用默认架构
        const defaultArch = 'linux/amd64';
        addLog(`使用默认架构: ${defaultArch}`);
        return defaultArch;
    }

    /**
     * 获取镜像的可用架构列表并缓存架构-SHA256映射
     * 功能:从API获取镜像支持的所有架构及其对应的SHA256
     * @returns {Promise<Array>} 可用架构列表
     */
    async function getAvailableArchitectures() {
        try {
            // 提取镜像信息
            const { imageName, imageTag } = extractImageInfo();
            if (!imageName) {
                addLog('无法提取镜像信息,跳过架构列表获取');
                return [];
            }

            // 检查是否已有缓存
            const cacheKey = `${imageName}:${imageTag}`;
            if (cachedArchitectures && cachedArchitectures.cacheKey === cacheKey) {
                addLog('使用缓存的架构信息');
                return cachedArchitectures.architectures;
            }

            addLog(`获取镜像架构列表: ${imageName}:${imageTag}`);

            // 调用API获取镜像清单
            const manifestUrl = `${API_BASE_URL}/manifest?image=${encodeURIComponent(imageName)}&tag=${encodeURIComponent(imageTag)}`;
            const response = await fetch(manifestUrl);
            
            if (!response.ok) {
                throw new Error(`获取镜像清单失败: ${response.status}`);
            }

            const data = await response.json();
            
            if (data.multiArch && data.availablePlatforms) {
                addLog(`发现多架构镜像,包含 ${data.availablePlatforms.length} 个架构`);
                
                // 缓存架构信息,包含架构和对应的SHA256/digest
                cachedArchitectures = {
                    cacheKey: cacheKey,
                    architectures: data.availablePlatforms.map(platform => platform.platform),
                    platformMap: new Map(data.availablePlatforms.map(platform => [
                        platform.platform, 
                        {
                            digest: platform.digest,
                            sha256: platform.digest ? platform.digest.replace('sha256:', '') : null
                        }
                    ]))
                };
                
                addLog(`已缓存 ${data.availablePlatforms.length} 个架构的SHA256映射`);
                return cachedArchitectures.architectures;
            } else {
                addLog('发现单架构镜像,使用默认架构列表');
                
                // 为单架构镜像创建缓存
                cachedArchitectures = {
                    cacheKey: cacheKey,
                    architectures: ['linux/amd64'],
                    platformMap: new Map([['linux/amd64', { digest: null, sha256: null }]])
                };
                
                return cachedArchitectures.architectures;
            }
        } catch (error) {
            addLog(`获取架构列表失败: ${error.message}`, 'error');
            return [];
        }
    }

    /**
     * 更新架构选择器的选项
     * 功能:根据获取到的可用架构更新下拉选择器选项
     * @param {HTMLElement} selector - 架构选择器元素
     * @param {Array} architectures - 可用架构列表
     */
    function updateArchSelectorOptions(selector, architectures) {
        if (!selector || !architectures || architectures.length === 0) {
            return;
        }

        // 保存当前选中的值
        const currentValue = selector.value;
        
        // 清空现有选项,但保留"自动检测"选项
        selector.innerHTML = '<option value="">自动检测</option>';
        
        // 添加获取到的架构选项
        architectures.forEach(arch => {
            const option = document.createElement('option');
            option.value = arch;
            option.textContent = arch;
            selector.appendChild(option);
        });
        
        // 如果之前有选中的值且仍然存在,恢复选中状态
        if (currentValue && architectures.includes(currentValue)) {
            selector.value = currentValue;
        }
        
        addLog(`架构选择器已更新: ${architectures.join(', ')}`);
    }

    /**
     * 设置页面架构选择器变化监听
     * 功能:监听Docker Hub页面上的OS/ARCH选择器变化,自动更新我们的架构选择器
     */
    function setupArchChangeListener() {
        // 监听所有可能的架构选择器变化
        const archSelectors = [
            'select', // 标准select元素
            '.MuiSelect-select', // MUI选择器
            '[role="combobox"]' // 下拉框角色
        ];

        archSelectors.forEach(selector => {
            const elements = document.querySelectorAll(selector);
            elements.forEach(element => {
                // 为每个可能的架构选择器添加变化监听
                if (element.tagName === 'SELECT') {
                    element.addEventListener('change', handleArchChange);
                    addLog(`为SELECT元素添加了变化监听: ${element.className}`);
                } else {
                    // 对于非select元素,使用MutationObserver监听内容变化
                    const observer = new MutationObserver(handleArchChange);
                    observer.observe(element, {
                        childList: true,
                        subtree: true,
                        characterData: true
                    });
                    addLog(`为元素添加了MutationObserver: ${element.className}`);
                }
            });
        });

        // 使用全局MutationObserver监听页面架构相关变化
        const globalObserver = new MutationObserver((mutations) => {
            mutations.forEach(mutation => {
                // 检查变化的节点是否包含架构信息
                if (mutation.type === 'childList' || mutation.type === 'characterData') {
                    const target = mutation.target;
                    if (target.textContent && target.textContent.match(/^(linux|windows|darwin)\/.+/i)) {
                        handleArchChange();
                    }
                }
            });
        });

        // 监听页面主要内容区域的变化
        const mainContent = document.querySelector('main, body');
        if (mainContent) {
            globalObserver.observe(mainContent, {
                childList: true,
                subtree: true,
                characterData: true
            });
        }

        addLog('架构变化监听器已设置');
    }

    /**
     * 处理页面架构选择器变化
     * 功能:当页面架构选择器发生变化时,自动更新我们的架构选择器
     */
    async function handleArchChange() {
        try {
            // 防止频繁触发
            if (handleArchChange.timeout) {
                clearTimeout(handleArchChange.timeout);
            }

            handleArchChange.timeout = setTimeout(async () => {
                addLog('检测到页面架构变化,正在更新...');
                
                const ourArchSelector = document.getElementById('archSelector');
                const archIndicator = document.getElementById('archIndicator');
                
                // 显示更新状态
                if (archIndicator) {
                    archIndicator.style.display = 'inline';
                    archIndicator.textContent = '🔄 更新中...';
                    archIndicator.style.color = '#007bff';
                }
                
                // 重新获取可用架构列表
                const availableArchs = await getAvailableArchitectures();
                if (availableArchs && availableArchs.length > 0 && ourArchSelector) {
                    updateArchSelectorOptions(ourArchSelector, availableArchs);
                }
                
                // 重新检测当前架构
                const detectedArch = await autoDetectArchitecture();
                if (detectedArch && ourArchSelector) {
                    // 检查选项中是否存在检测到的架构
                    const existingOption = ourArchSelector.querySelector(`option[value="${detectedArch}"]`);
                    if (existingOption) {
                        ourArchSelector.value = detectedArch;
                    } else {
                        // 如果选项中不存在,添加新选项
                        const newOption = document.createElement('option');
                        newOption.value = detectedArch;
                        newOption.textContent = detectedArch;
                        newOption.selected = true;
                        ourArchSelector.appendChild(newOption);
                    }
                    
                    ourArchSelector.title = `当前架构: ${detectedArch} (页面同步)`;
                    addLog(`架构选择器已同步更新为: ${detectedArch}`);
                    
                    // 更新指示器
                    if (archIndicator) {
                        archIndicator.textContent = `✅ ${detectedArch}`;
                        archIndicator.style.color = '#28a745';
                        setTimeout(() => {
                            archIndicator.style.display = 'none';
                        }, 2000);
                    }
                } else if (archIndicator) {
                    archIndicator.textContent = '❌ 更新失败';
                    archIndicator.style.color = '#dc3545';
                    setTimeout(() => {
                        archIndicator.style.display = 'none';
                    }, 2000);
                }
            }, 500); // 500ms防抖
        } catch (error) {
            addLog(`架构变化处理失败: ${error.message}`, 'error');
        }
    }

    // ==================== 脚本入口点 ====================
    
    // 脚本启动日志
    console.log('Docker Hub 镜像下载器脚本已加载');
    
    // 启动初始化流程
    initDownloader();
    
    // 设置页面变化监听
    setupPageChangeListener();

})();