Mobile AI Summary (MD3)

为移动端设计的AI页面总结工具,采用Material Design 3风格

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Mobile AI Summary (MD3)
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  为移动端设计的AI页面总结工具,采用Material Design 3风格
// @author       Justin Ye
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      api.openai.com
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    // 封装 GM_xmlhttpRequest 为 Promise 形式,解决跨域问题
    function gmFetch(url, options) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: options.method || 'GET',
                url: url,
                headers: options.headers,
                data: options.body,
                onload: (response) => {
                    if (response.status >= 200 && response.status < 300) {
                        resolve({
                            json: () => Promise.resolve(JSON.parse(response.responseText)),
                            text: () => Promise.resolve(response.responseText),
                            status: response.status
                        });
                    } else {
                        reject(new Error(`HTTP error! status: ${response.status} ${response.statusText || ''}\nResponse: ${response.responseText.substring(0, 100)}...`));
                    }
                },
                onerror: (error) => {
                    console.error('GM_xmlhttpRequest error:', error);
                    reject(new Error('Network error: Failed to fetch'));
                },
                ontimeout: () => {
                    reject(new Error('Request timeout'));
                }
            });
        });
    }

    // MD3 风格定义
    const md3Colors = {
        light: {
            primary: '#006492',
            onPrimary: '#ffffff',
            primaryContainer: '#cae6ff',
            onPrimaryContainer: '#001e30',
            surface: '#f8f9ff',
            onSurface: '#191c20',
            surfaceContainer: '#f0f4f8',
            outline: '#72777f',
            shadow: 'rgba(0, 0, 0, 0.2)',
            surfaceVariant: '#dde3ea',
            onSurfaceVariant: '#41484d'
        },
        dark: {
            primary: '#8ccceb',
            onPrimary: '#00344e',
            primaryContainer: '#004b6f',
            onPrimaryContainer: '#cae6ff',
            surface: '#111418',
            onSurface: '#e1e2e8',
            surfaceContainer: '#1d2024',
            outline: '#8c9199',
            shadow: 'rgba(0, 0, 0, 0.4)',
            surfaceVariant: '#41484d',
            onSurfaceVariant: '#c1c7ce'
        }
    };

    // 新的提示词
    const newPrompt = `你是一个专业的中文内容总结器。你的任务是分析提供的网页内容,**识别内容类型(例如:新闻报道、研究报告、普通文章、市场分析等)**,并在此基础上创建一个清晰、简洁、结构良好的中文总结。**总结必须严格依据原文内容,不得进行任何推测、假设或添加原文中未包含的信息。**

请严格遵守以下指南:

1.  **内容类型识别与结构确定:**
    *   首先识别原文的内容类型。
    *   根据内容类型,确定最合适的分段和总结重点。总结的结构应该逻辑清晰,反映原文的核心信息。
    *   你可以使用 **\`##\`** 作为主要分段的标题,例如:对于新闻可以使用“事件概述”、“关键进展”等,对于报告可以使用“主要发现”、“数据支持”等。不必拘泥于原文的固定分段,但要确保覆盖核心要点。

2.  **输出格式:**
    *   使用 **\`##\`** 表示主要段落标题。
    *   使用 **\`•\`** 表示段落内的关键点和细节。
    *   使用 **粗体** 突出重要术语、概念或关键信息。
    *   使用 **\`>\`** 表示引人注目的原文引述(如果适用)。

3.  **内容要求:**
    *   总结必须**严格忠于原文**,不允许加入任何个人观点、推测或假设。
    *   **识别并保留原文中的重要数据、数字、统计信息或关键事实。**
    *   根据识别的内容类型,调整总结的侧重点,但**所有信息必须来源于原文**。

4.  **写作风格:**
    *   语言清晰简洁。
    *   专业且客观的语调。
    *   逻辑流畅。
    *   易于理解。
    *   聚焦于原文的核心信息和重要细节。

5.  **重要规则:**
    *   **DO NOT show your reasoning process.** (不要显示你的思考过程或内部步骤。)`;

    // 默认配置
    const defaultConfig = {
        apiUrl: 'https://api.openai.com/v1/chat/completions',
        apiKey: '',
        prompt: newPrompt,
        model: 'gpt-3.5-turbo',
        theme: 'auto' // auto, light, dark
    };

    // GM 函数兼容层
    const GM = {
        setValue: (key, value) => {
            try {
                if (typeof GM_setValue !== 'undefined') {
                    GM_setValue(key, value);
                } else {
                    localStorage.setItem(`mobile_ai_summary_${key}`, JSON.stringify(value));
                }
            } catch (error) {
                console.error('保存配置失败:', error);
            }
        },
        getValue: (key, defaultValue) => {
            try {
                if (typeof GM_getValue !== 'undefined') {
                    return GM_getValue(key, defaultValue);
                }
                const value = localStorage.getItem(`mobile_ai_summary_${key}`);
                return value ? JSON.parse(value) : defaultValue;
            } catch (error) {
                console.error('获取配置失败:', error);
                return defaultValue;
            }
        }
    };

    // 获取配置
    let config = {
        apiUrl: GM.getValue('apiUrl', defaultConfig.apiUrl),
        apiKey: GM.getValue('apiKey', defaultConfig.apiKey),
        prompt: GM.getValue('prompt', defaultConfig.prompt),
        model: GM.getValue('model', defaultConfig.model),
        theme: GM.getValue('theme', defaultConfig.theme)
    };

    // 加载 marked.js
    let markedLoaded = false;
    const markedScript = document.createElement('script');
    markedScript.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
    markedScript.onload = () => {
        markedLoaded = true;
        marked.setOptions({
            breaks: true,
            gfm: true
        });
    };
    document.head.appendChild(markedScript);

    // 创建样式
    const style = document.createElement('style');
    style.textContent = `
        /* Material Design 3 风格样式 */
        :root {
            --mas-primary: ${md3Colors.light.primary};
            --mas-on-primary: ${md3Colors.light.onPrimary};
            --mas-primary-container: ${md3Colors.light.primaryContainer};
            --mas-on-primary-container: ${md3Colors.light.onPrimaryContainer};
            --mas-surface: ${md3Colors.light.surface};
            --mas-on-surface: ${md3Colors.light.onSurface};
            --mas-surface-container: ${md3Colors.light.surfaceContainer};
            --mas-outline: ${md3Colors.light.outline};
            --mas-shadow: ${md3Colors.light.shadow};
            --mas-surface-variant: ${md3Colors.light.surfaceVariant};
            --mas-on-surface-variant: ${md3Colors.light.onSurfaceVariant};
        }

        [data-theme="dark"] {
            --mas-primary: ${md3Colors.dark.primary};
            --mas-on-primary: ${md3Colors.dark.onPrimary};
            --mas-primary-container: ${md3Colors.dark.primaryContainer};
            --mas-on-primary-container: ${md3Colors.dark.onPrimaryContainer};
            --mas-surface: ${md3Colors.dark.surface};
            --mas-on-surface: ${md3Colors.dark.onSurface};
            --mas-surface-container: ${md3Colors.dark.surfaceContainer};
            --mas-outline: ${md3Colors.dark.outline};
            --mas-shadow: ${md3Colors.dark.shadow};
            --mas-surface-variant: ${md3Colors.dark.surfaceVariant};
            --mas-on-surface-variant: ${md3Colors.dark.onSurfaceVariant};
        }

        /* Pixel 风格优化 */
        .mas-fab-container {
            position: fixed;
            left: 16px; /* 移到左侧 */
            bottom: 80px; /* 避开底部导航栏 */
            z-index: 999999;
            display: flex;
            flex-direction: column;
            gap: 16px;
            pointer-events: none; /* 允许点击穿透 */
        }

        .mas-fab {
            width: 48px; /* 缩小尺寸 */
            height: 48px;
            border-radius: 16px; /* 调整圆角以匹配新尺寸 */
            background-color: var(--mas-primary-container);
            color: var(--mas-on-primary-container);
            border: none;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            cursor: pointer;
            pointer-events: auto;
            transition: all 0.2s cubic-bezier(0.2, 0, 0, 1);
            -webkit-tap-highlight-color: transparent;
        }

        .mas-fab:active {
            transform: scale(0.92);
            box-shadow: 0 2px 6px rgba(0,0,0,0.1);
        }

        .mas-fab svg {
            width: 24px;
            height: 24px;
            fill: currentColor;
        }

        /* Bottom Sheet 样式 */
        .mas-bottom-sheet {
            position: fixed;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--mas-surface-container);
            border-top-left-radius: 28px;
            border-top-right-radius: 28px;
            box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
            z-index: 1000000;
            transform: translateY(100%);
            transition: transform 0.35s cubic-bezier(0.2, 0, 0, 1);
            max-height: 85vh;
            display: flex;
            flex-direction: column;
            color: var(--mas-on-surface);
            font-family: 'Google Sans', system-ui, -apple-system, sans-serif; /* 尝试使用 Google Sans */
        }

        .mas-bottom-sheet.show {
            transform: translateY(0);
        }

        .mas-drag-handle {
            width: 32px;
            height: 4px;
            background-color: var(--mas-outline);
            opacity: 0.4;
            border-radius: 2px;
            margin: 16px auto 0;
            flex-shrink: 0;
        }

        .mas-sheet-content {
            padding: 24px;
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
        }

        .mas-sheet-header {
            font-size: 22px;
            font-weight: 400;
            margin-bottom: 24px;
            color: var(--mas-on-surface);
            font-weight: 500;
        }

        .mas-icon-btn {
            color: var(--mas-on-surface-variant);
            background: transparent;
            border: none;
            padding: 8px;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.2s;
        }

        .mas-icon-btn:hover {
            background-color: rgba(0, 0, 0, 0.08);
        }

        .mas-icon-btn:active {
            background-color: rgba(0, 0, 0, 0.12);
        }

        [data-theme="dark"] .mas-icon-btn:hover {
            background-color: rgba(255, 255, 255, 0.08);
        }

        [data-theme="dark"] .mas-icon-btn:active {
            background-color: rgba(255, 255, 255, 0.12);
        }

        /* 表单样式 */
        .mas-input-group {
            margin-bottom: 20px;
        }

        .mas-label {
            display: block;
            font-size: 12px;
            color: var(--mas-on-surface-variant);
            margin-bottom: 8px;
            font-weight: 500;
        }

        .mas-input, .mas-textarea, .mas-select {
            width: 100%;
            padding: 16px;
            border: 1px solid var(--mas-outline);
            border-radius: 12px; /* 更圆润的输入框 */
            background: var(--mas-surface);
            font-size: 16px;
            color: var(--mas-on-surface);
            box-sizing: border-box;
            transition: all 0.2s;
            font-family: inherit;
        }

        .mas-input:focus, .mas-textarea:focus, .mas-select:focus {
            outline: none;
            border-color: var(--mas-primary);
            background: var(--mas-surface-container);
            box-shadow: 0 0 0 2px var(--mas-primary-container);
        }

        .mas-textarea {
            min-height: 100px;
            resize: vertical;
            font-family: inherit;
        }

        /* 按钮样式 */
        .mas-btn {
            background-color: var(--mas-primary);
            color: var(--mas-on-primary);
            border: none;
            border-radius: 24px; /* 完全圆角 */
            padding: 0 24px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            width: 100%;
            height: 48px; /* 更高的点击区域 */
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            letter-spacing: 0.5px;
        }

        .mas-btn:active {
            opacity: 0.9;
            transform: scale(0.98);
        }

        .mas-btn-text {
            background: transparent;
            color: var(--mas-primary);
            margin-top: 8px;
        }

        /* 结果内容样式 */
        .mas-result-content {
            font-size: 16px;
            line-height: 1.6;
        }
        
        .mas-result-content h1, .mas-result-content h2, .mas-result-content h3 {
            margin-top: 1em;
            margin-bottom: 0.5em;
            color: var(--mas-on-surface);
        }

        .mas-result-content p {
            margin-bottom: 1em;
            color: var(--mas-on-surface-variant);
        }

        .mas-result-content ul {
            padding-left: 20px;
            margin-bottom: 1em;
            color: var(--mas-on-surface-variant);
        }

        .mas-result-content li {
            margin-bottom: 0.5em;
        }

        .mas-result-content strong {
            font-weight: 700;
            color: var(--mas-on-surface);
        }

        .mas-result-content blockquote {
            border-left: 4px solid var(--mas-primary);
            background-color: var(--mas-surface-variant);
            margin: 1em 0;
            padding: 12px 16px;
            border-radius: 0 12px 12px 0;
            color: var(--mas-on-surface-variant);
        }

        /* 遮罩层 */
        .mas-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.32);
            z-index: 999999;
            display: none;
            opacity: 0;
            transition: opacity 0.3s;
        }

        .mas-overlay.show {
            display: block;
            opacity: 1;
        }

        .mas-actions {
            display: flex;
            gap: 12px;
            margin-top: 24px;
        }
    `;
    document.head.appendChild(style);

    // 创建 UI 元素
    const fabContainer = document.createElement('div');
    fabContainer.className = 'mas-fab-container';
    
    // 主 FAB (合并了总结和设置入口)
    const mainFab = document.createElement('button');
    mainFab.className = 'mas-fab';
    mainFab.innerHTML = `
        <svg viewBox="0 0 24 24">
            <path d="M19,3H5C3.89,3,3,3.89,3,5v14c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.89,20.11,3,19,3z M19,19H5V5h14V19z M7,7h10v2H7V7z M7,11h10v2H7V11z M7,15h7v2H7V15z"/>
        </svg>
    `;
    
    fabContainer.appendChild(mainFab);
    document.body.appendChild(fabContainer);

    // 遮罩层
    const overlay = document.createElement('div');
    overlay.className = 'mas-overlay';
    document.body.appendChild(overlay);

    // 主面板 Bottom Sheet (包含首页、设置、结果)
    const mainSheet = document.createElement('div');
    mainSheet.className = 'mas-bottom-sheet';
    
    // 应用主题
    function applyTheme() {
        const theme = config.theme;
        if (theme === 'dark') {
            mainSheet.setAttribute('data-theme', 'dark');
        } else if (theme === 'light') {
            mainSheet.setAttribute('data-theme', 'light');
        } else {
            // Auto
            if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
                mainSheet.setAttribute('data-theme', 'dark');
            } else {
                mainSheet.setAttribute('data-theme', 'light');
            }
        }
    }

    // 渲染主面板内容
    function renderSheetContent(view = 'home', data = null) {
        let content = '';
        applyTheme();
        
        if (view === 'home') {
            content = `
                <div class="mas-drag-handle"></div>
                <div class="mas-sheet-content">
                    <div class="mas-sheet-header">
                        <span>AI 页面总结</span>
                        <!-- Settings button removed from here -->
                    </div>
                    <div style="margin-bottom: 24px;">
                        <p style="margin-bottom: 8px; font-size: 14px; color: var(--mas-on-surface-variant);">当前页面:</p>
                        <p style="font-weight: 500; line-height: 1.4; color: var(--mas-on-surface);">${document.title}</p>
                    </div>
                    <button class="mas-btn" id="masStartSummary">
                        <svg style="width:20px;height:20px;margin-right:8px;fill:currentColor" viewBox="0 0 24 24">
                            <path d="M14,17H7V15H14M17,13H7V11H17M17,9H7V7H17M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z" />
                        </svg>
                        开始总结
                    </button>
                </div>
            `;
        } else if (view === 'settings') {
            content = `
                <div class="mas-drag-handle"></div>
                <div class="mas-sheet-content">
                    <div class="mas-sheet-header">
                        <span>设置</span>
                        <button class="mas-icon-btn" id="masBackHome">
                            <svg style="width:24px;height:24px;fill:currentColor" viewBox="0 0 24 24">
                                <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
                            </svg>
                        </button>
                    </div>
                    <div class="mas-input-group">
                        <label class="mas-label">主题</label>
                        <select class="mas-select" id="masTheme">
                            <option value="auto" ${config.theme === 'auto' ? 'selected' : ''}>跟随系统</option>
                            <option value="light" ${config.theme === 'light' ? 'selected' : ''}>浅色模式</option>
                            <option value="dark" ${config.theme === 'dark' ? 'selected' : ''}>深色模式</option>
                        </select>
                    </div>
                    <div class="mas-input-group">
                        <label class="mas-label">API URL</label>
                        <input type="text" class="mas-input" id="masApiUrl" value="${config.apiUrl}">
                    </div>
                    <div class="mas-input-group">
                        <label class="mas-label">API Key</label>
                        <input type="password" class="mas-input" id="masApiKey" value="${config.apiKey}">
                    </div>
                    <div class="mas-input-group">
                        <label class="mas-label">模型</label>
                        <input type="text" class="mas-input" id="masModel" value="${config.model}">
                    </div>
                    <div class="mas-input-group">
                        <label class="mas-label">提示词</label>
                        <textarea class="mas-textarea" id="masPrompt">${config.prompt}</textarea>
                    </div>
                    <button class="mas-btn" id="masSaveConfig">保存并返回</button>
                </div>
            `;
        } else if (view === 'result') {
            content = `
                <div class="mas-drag-handle"></div>
                <div class="mas-sheet-content">
                    <div class="mas-sheet-header">
                        <span>总结结果</span>
                        <!-- 关闭按钮已移除 -->
                    </div>
                    <div id="masResultContent" class="mas-result-content">${data || '<p>正在生成...</p>'}</div>
                    <div class="mas-actions">
                        <button class="mas-btn" id="masCopyBtn">复制内容</button>
                    </div>
                </div>
            `;
        }
        
        mainSheet.innerHTML = content;
        bindEvents(view);
    }

    function bindEvents(view) {
        if (view === 'home') {
            // Settings button event removed
            document.getElementById('masStartSummary').addEventListener('click', startSummary);
        } else if (view === 'settings') {
            document.getElementById('masBackHome').addEventListener('click', () => {
                renderSheetContent('home');
            });
            document.getElementById('masSaveConfig').addEventListener('click', () => {
                config.apiUrl = document.getElementById('masApiUrl').value;
                config.apiKey = document.getElementById('masApiKey').value;
                config.prompt = document.getElementById('masPrompt').value;
                config.model = document.getElementById('masModel').value;
                config.theme = document.getElementById('masTheme').value;

                GM.setValue('apiUrl', config.apiUrl);
                GM.setValue('apiKey', config.apiKey);
                GM.setValue('prompt', config.prompt);
                GM.setValue('model', config.model);
                GM.setValue('theme', config.theme);
                
                applyTheme(); // 立即应用主题
                alert('配置已保存');
                renderSheetContent('home');
            });
        } else if (view === 'result') {
            // 移除关闭按钮事件绑定
            
            const copyBtn = document.getElementById('masCopyBtn');
            if (copyBtn) {
                copyBtn.addEventListener('click', () => {
                    const text = document.getElementById('masResultContent').innerText;
                    navigator.clipboard.writeText(text).then(() => {
                        const originalText = copyBtn.innerText;
                        copyBtn.innerText = '已复制';
                        setTimeout(() => copyBtn.innerText = originalText, 2000);
                    });
                });
            }
        }
    }

    document.body.appendChild(mainSheet);

    // 交互逻辑
    function showOverlay() {
        overlay.classList.add('show');
        document.body.style.overflow = 'hidden';
    }

    function hideOverlay() {
        overlay.classList.remove('show');
        document.body.style.overflow = '';
    }

    // 点击遮罩关闭
    overlay.addEventListener('click', () => {
        mainSheet.classList.remove('show');
        hideOverlay();
        setTimeout(() => renderSheetContent('home'), 300);
    });

    // 主按钮交互:单击总结,长按设置
    let longPressTimer;
    let isLongPress = false;

    mainFab.addEventListener('touchstart', (e) => {
        isLongPress = false;
        longPressTimer = setTimeout(() => {
            isLongPress = true;
            // 触发振动反馈
            if (navigator.vibrate) {
                navigator.vibrate(50);
            }
            renderSheetContent('settings');
            mainSheet.classList.add('show');
            showOverlay();
        }, 500); // 500ms 定义为长按
    });

    mainFab.addEventListener('touchend', (e) => {
        // Prevent the browser from firing a "ghost" click event after the touch ends.
        // This was causing the overlay to immediately close the panel.
        e.preventDefault();
        clearTimeout(longPressTimer);
        if (!isLongPress) {
            // 这是单击
            startSummary();
        }
    });
    
    mainFab.addEventListener('touchmove', () => {
        // 如果手指移动,取消长按计时器
        clearTimeout(longPressTimer);
    });

    // 为桌面端保留单击和右键单击行为
    mainFab.addEventListener('click', () => {
        // 在桌面端,单击直接总结
        if (!('ontouchstart' in window)) {
            startSummary();
        }
    });

    mainFab.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        // 在桌面端,右键单击打开设置
        if (!('ontouchstart' in window)) {
            renderSheetContent('settings');
            mainSheet.classList.add('show');
            showOverlay();
        }
    });

    // 开始总结逻辑
    async function startSummary() {
        if (!config.apiKey) {
            alert('请先配置 API Key');
            renderSheetContent('settings');
            mainSheet.classList.add('show');
            showOverlay();
            return;
        }

        renderSheetContent('result', '<p>正在分析页面内容...</p>');
        mainSheet.classList.add('show');
        showOverlay();
        
        const pageContent = document.body.innerText.substring(0, 5000);

        try {
            // 使用封装的 gmFetch 替代原生 fetch
            const response = await gmFetch(config.apiUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${config.apiKey}`
                },
                body: JSON.stringify({
                    model: config.model,
                    messages: [
                        {
                            role: 'system',
                            content: config.prompt
                        },
                        {
                            role: 'user',
                            content: pageContent
                        }
                    ]
                })
            });

            const data = await response.json();
            if (data.choices && data.choices[0]) {
                const rawContent = data.choices[0].message.content;
                
                // 等待 marked 加载
                if (!markedLoaded) {
                    await new Promise(resolve => setTimeout(resolve, 500));
                }

                let htmlContent = rawContent;
                try {
                    htmlContent = marked.parse(rawContent);
                } catch (e) {
                    console.error('Markdown parse error', e);
                }
                
                // 更新结果视图内容
                const contentDiv = document.getElementById('masResultContent');
                if (contentDiv) {
                    contentDiv.innerHTML = htmlContent;
                }
            } else {
                throw new Error('API 返回数据异常');
            }
        } catch (error) {
            const contentDiv = document.getElementById('masResultContent');
            if (contentDiv) {
                contentDiv.innerHTML = `<p style="color: red;">出错啦: ${error.message}</p>`;
            }
        }
    }

    // 简单的拖拽支持 (FAB)
    let isDragging = false;
    let startY = 0;
    let startBottom = 100;

    fabContainer.addEventListener('touchstart', (e) => {
        if (e.target.closest('.mas-fab')) {
            isDragging = true;
            startY = e.touches[0].clientY;
            const style = window.getComputedStyle(fabContainer);
            startBottom = parseInt(style.bottom);
        }
    });

    document.addEventListener('touchmove', (e) => {
        if (!isDragging) return;
        e.preventDefault(); // 防止滚动
        const deltaY = startY - e.touches[0].clientY;
        let newBottom = startBottom + deltaY;
        newBottom = Math.max(20, Math.min(newBottom, window.innerHeight - 100));
        fabContainer.style.bottom = `${newBottom}px`;
    }, { passive: false });

    document.addEventListener('touchend', () => {
        isDragging = false;
    });

})();