Y
K
N
U
F

boxed

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DeepSeek Web客户端(含LaTeX渲染)</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <!-- KaTeX CSS for LaTeX rendering -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" crossorigin="anonymous">
    <style>
        /* 样式完全保持不变,与原文件相同 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
        }

        :root {
            --primary: #4a90e2;
            --primary-dark: #3a7bc8;
            --success: #34c759;
            --warning: #ff9500;
            --danger: #ff3b30;
            --light-bg: #f8f9fa;
            --dark-bg: #1a1a1a;
            --sidebar-bg: #2c3e50;
            --card-bg: #ffffff;
            --text: #333333;
            --text-light: #666666;
            --border: #e0e0e0;
            --shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            --code-bg: #2c3e50;
            --code-color: #ecf0f1;
            --message-user: #e3f2fd;
            --message-ai: #ffffff;
            --message-system: #f0f7ff;
        }

        body {
            background-color: var(--light-bg);
            color: var(--text);
            height: 100vh;
            overflow: hidden;
        }

        .app-container {
            display: flex;
            height: 100vh;
        }

        /* 侧边栏 */
        .sidebar {
            width: 280px;
            background-color: var(--sidebar-bg);
            color: white;
            display: flex;
            flex-direction: column;
            padding: 20px 0;
            flex-shrink: 0;
            overflow-y: auto;
        }

        .logo {
            padding: 0 20px 20px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
            text-align: center;
        }

        .logo h1 {
            font-size: 22px;
            font-weight: 700;
            margin-bottom: 5px;
            color: #fff;
        }

        .logo .version {
            font-size: 12px;
            color: rgba(255, 255, 255, 0.7);
        }

        .menu {
            padding: 20px;
            flex-grow: 1;
        }

        .menu-section {
            margin-bottom: 25px;
        }

        .menu-section h3 {
            font-size: 14px;
            text-transform: uppercase;
            color: rgba(255, 255, 255, 0.5);
            margin-bottom: 12px;
            letter-spacing: 1px;
        }

        .menu-item {
            display: flex;
            align-items: center;
            padding: 10px 15px;
            border-radius: 8px;
            margin-bottom: 5px;
            cursor: pointer;
            transition: all 0.2s;
            color: rgba(255, 255, 255, 0.8);
        }

        .menu-item:hover {
            background-color: rgba(255, 255, 255, 0.1);
            color: white;
        }

        .menu-item.active {
            background-color: var(--primary);
            color: white;
        }

        .menu-item i {
            width: 24px;
            margin-right: 10px;
            font-size: 16px;
        }

        .menu-item span {
            font-size: 14px;
            font-weight: 500;
        }

        .status-bar {
            padding: 15px 20px;
            border-top: 1px solid rgba(255, 255, 255, 0.1);
            font-size: 12px;
        }

        .status-item {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
            color: rgba(255, 255, 255, 0.7);
        }

        .status-value {
            font-weight: 600;
            color: white;
        }

        /* 主内容区 */
        .main-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .header {
            padding: 16px 24px;
            background-color: var(--card-bg);
            border-bottom: 1px solid var(--border);
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
            flex-shrink: 0;
        }

        .chat-title {
            font-size: 18px;
            font-weight: 600;
            color: var(--text);
        }

        .header-actions {
            display: flex;
            gap: 10px;
        }

        .header-btn {
            padding: 8px 16px;
            background-color: var(--light-bg);
            border: 1px solid var(--border);
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            color: var(--text);
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 6px;
        }

        .header-btn:hover {
            background-color: var(--primary);
            color: white;
            border-color: var(--primary);
        }

        .chat-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .messages-container {
            flex: 1;
            overflow-y: auto;
            padding: 24px;
            background-color: var(--light-bg);
            background-image: 
                radial-gradient(circle at 25px 25px, rgba(0, 0, 0, 0.05) 2%, transparent 0%), 
                radial-gradient(circle at 75px 75px, rgba(0, 0, 0, 0.05) 2%, transparent 0%);
            background-size: 100px 100px;
        }

        .message {
            margin-bottom: 24px;
            max-width: 85%;
            animation: fadeIn 0.3s ease-out;
            position: relative;
        }

        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }

        .user-message {
            margin-left: auto;
        }

        .assistant-message {
            margin-right: auto;
        }

        .system-message {
            margin-left: auto;
            margin-right: auto;
            max-width: 90%;
        }

        .message-header {
            display: flex;
            align-items: center;
            margin-bottom: 8px;
            flex-wrap: wrap;
            gap: 8px;
        }

        .message-avatar {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 12px;
            font-weight: 600;
            font-size: 14px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            transition: transform 0.2s;
        }

        .assistant-message .message-avatar {
            cursor: pointer;
        }

        .assistant-message .message-avatar:hover {
            transform: scale(1.05);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
        }

        .user-avatar {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }

        .assistant-avatar {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
            color: white;
        }

        .system-avatar {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
        }

        .message-sender {
            font-weight: 600;
            font-size: 14px;
            color: var(--text);
        }

        .user-sender {
            color: #667eea;
        }

        .assistant-sender {
            color: #f5576c;
        }

        .system-sender {
            color: #4facfe;
        }

        .message-time {
            font-size: 12px;
            color: var(--text-light);
            margin-left: auto;
        }

        .message-actions {
            display: flex;
            gap: 6px;
            margin-left: auto;
        }

        .message-action-btn {
            background: none;
            border: none;
            color: var(--text-light);
            cursor: pointer;
            font-size: 14px;
            padding: 4px 8px;
            border-radius: 4px;
            transition: all 0.2s;
            display: inline-flex;
            align-items: center;
            gap: 4px;
        }

        .message-action-btn:hover {
            background-color: var(--light-bg);
            color: var(--primary);
        }

        .message-content {
            padding: 16px 20px;
            border-radius: 12px;
            line-height: 1.6;
            font-size: 15px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
            white-space: pre-wrap;
            overflow-wrap: break-word;
            position: relative;
        }

        .user-content {
            background: linear-gradient(135deg, var(--message-user) 0%, #d6e8ff 100%);
            color: #2c3e50;
            border-top-right-radius: 4px;
            border-left: 4px solid #667eea;
        }

        .user-content:before {
            content: '';
            position: absolute;
            top: 0;
            right: -10px;
            width: 0;
            height: 0;
            border-left: 10px solid #d6e8ff;
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
        }

        .assistant-content {
            background: linear-gradient(135deg, var(--message-ai) 0%, #f9f9f9 100%);
            color: var(--text);
            border-top-left-radius: 4px;
            border-left: 4px solid #f5576c;
            border: 1px solid #f0f0f0;
        }

        .assistant-content:before {
            content: '';
            position: absolute;
            top: 0;
            left: -10px;
            width: 0;
            height: 0;
            border-right: 10px solid #f9f9f9;
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
        }

        .assistant-content.raw-text {
            font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
            font-size: 14px;
            white-space: pre-wrap;
            line-height: 1.5;
            background-color: #f9f9f9;
            padding: 12px 16px;
            border-left: 4px solid var(--primary);
        }

        .system-content {
            background: linear-gradient(135deg, var(--message-system) 0%, #e8f4ff 100%);
            color: #2c3e50;
            border-radius: 12px;
            border-left: 4px solid #4facfe;
            font-style: italic;
        }

        .reasoning-content {
            background-color: rgba(0, 0, 0, 0.05);
            color: var(--text-light);
            font-style: italic;
            padding: 12px 16px;
            border-radius: 8px;
            margin-bottom: 12px;
            font-size: 14px;
            border-left: 4px solid var(--warning);
            white-space: pre-wrap;
            background: linear-gradient(135deg, #fff8e1 0%, #fff3cd 100%);
        }

        .edit-textarea {
            width: 100%;
            padding: 12px;
            border: 2px solid var(--primary);
            border-radius: 8px;
            font-size: 15px;
            font-family: inherit;
            resize: vertical;
            margin-bottom: 8px;
        }

        .edit-actions {
            display: flex;
            gap: 8px;
            justify-content: flex-end;
        }

        .edit-btn {
            padding: 6px 16px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.2s;
        }

        .edit-save {
            background-color: var(--primary);
            color: white;
        }

        .edit-save:hover {
            background-color: var(--primary-dark);
        }

        .edit-cancel {
            background-color: var(--light-bg);
            color: var(--text);
            border: 1px solid var(--border);
        }

        .edit-cancel:hover {
            background-color: #e0e0e0;
        }

        .empty-chat {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100%;
            color: var(--text-light);
            text-align: center;
            padding: 40px;
        }

        .empty-chat i {
            font-size: 64px;
            margin-bottom: 20px;
            color: var(--border);
        }

        .empty-chat h3 {
            font-size: 20px;
            margin-bottom: 10px;
            color: var(--text);
        }

        .empty-chat p {
            max-width: 400px;
            line-height: 1.6;
        }

        .input-container {
            padding: 20px 24px;
            background-color: var(--card-bg);
            border-top: 1px solid var(--border);
            flex-shrink: 0;
            box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
        }

        .input-area {
            display: flex;
            gap: 12px;
            position: relative;
        }

        .message-input {
            flex: 1;
            padding: 14px 18px;
            background-color: var(--light-bg);
            border: 2px solid var(--border);
            border-radius: 12px;
            color: var(--text);
            font-size: 15px;
            resize: none;
            min-height: 54px;
            max-height: 200px;
            line-height: 1.5;
            transition: all 0.3s;
            font-family: inherit;
            white-space: pre-wrap;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
        }

        .message-input:focus {
            outline: none;
            border-color: var(--primary);
            box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1), 0 4px 12px rgba(0, 0, 0, 0.1);
        }

        .send-button {
            padding: 0 24px;
            background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
            border: none;
            color: white;
            border-radius: 12px;
            cursor: pointer;
            font-size: 15px;
            font-weight: 600;
            align-self: flex-end;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 8px;
            min-height: 54px;
            box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
        }

        .send-button:hover:not(:disabled) {
            transform: translateY(-2px);
            box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
        }

        .send-button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
            transform: none;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
        }

        .input-actions {
            display: flex;
            justify-content: space-between;
            margin-top: 12px;
            padding: 0 8px;
        }

        .action-btn {
            background: none;
            border: none;
            color: var(--text-light);
            cursor: pointer;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 8px 12px;
            border-radius: 8px;
            transition: all 0.2s;
            background-color: rgba(0, 0, 0, 0.02);
        }

        .action-btn:hover {
            background-color: var(--light-bg);
            color: var(--text);
            transform: translateY(-1px);
        }

        .input-hint {
            font-size: 12px;
            color: var(--text-light);
            margin-top: 8px;
            text-align: center;
            font-style: italic;
        }

        /* 设置面板 */
        .settings-panel {
            width: 350px;
            background-color: var(--card-bg);
            border-left: 1px solid var(--border);
            padding: 0;
            display: none;
            flex-direction: column;
            overflow-y: auto;
            flex-shrink: 0;
            box-shadow: -2px 0 12px rgba(0, 0, 0, 0.1);
        }

        .settings-panel.open {
            display: flex;
            animation: slideIn 0.3s ease-out;
        }

        @keyframes slideIn {
            from { opacity: 0; transform: translateX(20px); }
            to { opacity: 1; transform: translateX(0); }
        }

        .settings-header {
            padding: 20px;
            border-bottom: 1px solid var(--border);
            background: linear-gradient(135deg, var(--light-bg) 0%, #eef5ff 100%);
        }

        .settings-title {
            font-size: 20px;
            font-weight: 700;
            color: var(--text);
            margin-bottom: 5px;
        }

        .settings-subtitle {
            font-size: 14px;
            color: var(--text-light);
        }

        .settings-content {
            padding: 20px;
            flex-grow: 1;
            overflow-y: auto;
        }

        .settings-group {
            margin-bottom: 30px;
            background-color: white;
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
        }

        .settings-group-title {
            font-size: 16px;
            font-weight: 600;
            color: var(--text);
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid var(--primary);
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .settings-group-title i {
            color: var(--primary);
        }

        .setting-item {
            margin-bottom: 18px;
        }

        .setting-label {
            display: block;
            margin-bottom: 8px;
            font-size: 14px;
            font-weight: 500;
            color: var(--text);
        }

        .setting-description {
            font-size: 12px;
            color: var(--text-light);
            margin-top: 4px;
            line-height: 1.4;
        }

        .setting-control {
            width: 100%;
            padding: 10px 14px;
            background-color: var(--light-bg);
            border: 1px solid var(--border);
            border-radius: 8px;
            color: var(--text);
            font-size: 14px;
            transition: all 0.2s;
        }

        .setting-control:focus {
            outline: none;
            border-color: var(--primary);
            box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.1);
        }

        .slider-container {
            display: flex;
            align-items: center;
            gap: 12px;
        }

        input[type="range"] {
            flex: 1;
            height: 6px;
            -webkit-appearance: none;
            background: var(--border);
            border-radius: 3px;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: var(--primary);
            cursor: pointer;
            border: 3px solid white;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
        }

        .slider-value {
            min-width: 40px;
            text-align: center;
            font-size: 14px;
            font-weight: 600;
            color: var(--primary);
            background-color: var(--light-bg);
            padding: 4px 8px;
            border-radius: 4px;
        }

        .checkbox-container {
            display: flex;
            align-items: center;
            gap: 10px;
            cursor: pointer;
            padding: 8px 0;
        }

        .checkbox-container input[type="checkbox"] {
            width: 18px;
            height: 18px;
            cursor: pointer;
        }

        .checkbox-container label {
            font-size: 14px;
            color: var(--text);
            cursor: pointer;
            flex-grow: 1;
        }

        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
        }

        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 24px;
        }

        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }

        input:checked + .toggle-slider {
            background-color: var(--primary);
        }

        input:checked + .toggle-slider:before {
            transform: translateX(26px);
        }

        .settings-footer {
            padding: 20px;
            border-top: 1px solid var(--border);
            background: linear-gradient(135deg, var(--light-bg) 0%, #eef5ff 100%);
        }

        .settings-btn {
            width: 100%;
            padding: 12px;
            background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
            border: none;
            color: white;
            border-radius: 8px;
            cursor: pointer;
            font-size: 16px;
            font-weight: 600;
            transition: all 0.2s;
            box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11);
        }

        .settings-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1);
        }

        .settings-btn.secondary {
            background: linear-gradient(135deg, var(--light-bg) 0%, #e0e0e0 100%);
            color: var(--text);
            border: 1px solid var(--border);
            margin-top: 10px;
        }

        .settings-btn.secondary:hover {
            background: linear-gradient(135deg, #e0e0e0 0%, #d0d0d0 100%);
        }

        /* 提示信息 */
        .alert {
            padding: 12px 16px;
            border-radius: 8px;
            margin-bottom: 20px;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 10px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        }

        .alert-info {
            background: linear-gradient(135deg, rgba(74, 144, 226, 0.1) 0%, rgba(74, 144, 226, 0.2) 100%);
            border-left: 4px solid var(--primary);
            color: var(--primary);
        }

        .alert-success {
            background: linear-gradient(135deg, rgba(52, 199, 89, 0.1) 0%, rgba(52, 199, 89, 0.2) 100%);
            border-left: 4px solid var(--success);
            color: var(--success);
        }

        .alert-warning {
            background: linear-gradient(135deg, rgba(255, 149, 0, 0.1) 0%, rgba(255, 149, 0, 0.2) 100%);
            border-left: 4px solid var(--warning);
            color: var(--warning);
        }

        .alert-danger {
            background: linear-gradient(135deg, rgba(255, 59, 48, 0.1) 0%, rgba(255, 59, 48, 0.2) 100%);
            border-left: 4px solid var(--danger);
            color: var(--danger);
        }

        /* 滚动条样式 */
        ::-webkit-scrollbar {
            width: 8px;
        }

        ::-webkit-scrollbar-track {
            background: #f0f0f0;
            border-radius: 4px;
        }

        ::-webkit-scrollbar-thumb {
            background: #c0c0c0;
            border-radius: 4px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: #a0a0a0;
        }

        /* 响应式设计 */
        @media (max-width: 1024px) {
            .sidebar {
                width: 80px;
            }
            
            .menu-item span, .logo h1, .logo .version, .status-item span:first-child {
                display: none;
            }
            
            .logo {
                padding: 20px 10px;
            }
            
            .menu-item {
                justify-content: center;
                padding: 15px 0;
            }
            
            .menu-item i {
                margin-right: 0;
                font-size: 18px;
            }
            
            .status-bar {
                padding: 15px 10px;
            }
            
            .settings-panel {
                position: absolute;
                right: 0;
                top: 0;
                bottom: 0;
                width: 100%;
                max-width: 400px;
                z-index: 100;
            }
        }

        @media (max-width: 768px) {
            .message {
                max-width: 95%;
            }
            
            .header {
                padding: 12px 16px;
            }
            
            .header-actions {
                flex-direction: column;
                gap: 5px;
            }
            
            .header-btn {
                padding: 6px 10px;
                font-size: 12px;
            }
        }

        /* 打字机效果 */
        .typing-indicator {
            display: inline-flex;
            align-items: center;
            padding: 12px 16px;
            background: linear-gradient(135deg, var(--light-bg) 0%, #f0f0f0 100%);
            border-radius: 12px;
            font-size: 14px;
            color: var(--text-light);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
        }

        .typing-dots {
            display: inline-flex;
            margin-left: 5px;
        }

        .typing-dots span {
            width: 4px;
            height: 4px;
            border-radius: 50%;
            background-color: var(--text-light);
            margin: 0 1px;
            animation: typing 1.4s infinite both;
        }

        .typing-dots span:nth-child(2) {
            animation-delay: 0.2s;
        }

        .typing-dots span:nth-child(3) {
            animation-delay: 0.4s;
        }

        @keyframes typing {
            0%, 60%, 100% { transform: translateY(0); }
            30% { transform: translateY(-5px); }
        }
        
        /* Markdown渲染样式 */
        .markdown-content {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
            line-height: 1.6;
            overflow-wrap: break-word;
        }
        
        .markdown-content h1, 
        .markdown-content h2, 
        .markdown-content h3, 
        .markdown-content h4, 
        .markdown-content h5, 
        .markdown-content h6 {
            margin-top: 1.2em;
            margin-bottom: 0.6em;
            font-weight: 600;
            line-height: 1.25;
        }
        
        .markdown-content h1 { 
            font-size: 1.8em; 
            border-bottom: 2px solid var(--border); 
            padding-bottom: 0.3em; 
            margin-top: 0.8em;
        }
        
        .markdown-content h2 { 
            font-size: 1.5em; 
            border-bottom: 1px solid var(--border); 
            padding-bottom: 0.3em; 
            margin-top: 1em;
        }
        
        .markdown-content h3 { 
            font-size: 1.3em; 
            margin-top: 1.2em;
        }
        
        .markdown-content h4 { font-size: 1.1em; }
        .markdown-content h5 { font-size: 1em; color: var(--text-light); }
        .markdown-content h6 { font-size: 0.9em; color: var(--text-light); }
        
        .markdown-content p {
            margin-bottom: 1em;
            line-height: 1.6;
        }
        
        .markdown-content ul, 
        .markdown-content ol {
            margin-bottom: 1em;
            padding-left: 2em;
        }
        
        .markdown-content li {
            margin-bottom: 0.5em;
            line-height: 1.5;
        }
        
        .markdown-content li > ul,
        .markdown-content li > ol {
            margin-top: 0.5em;
            margin-bottom: 0.5em;
        }
        
        .markdown-content blockquote {
            margin: 0 0 1em 0;
            padding: 12px 16px;
            color: var(--text-light);
            border-left: 4px solid var(--border);
            font-style: italic;
            background-color: rgba(0, 0, 0, 0.02);
            border-radius: 0 8px 8px 0;
        }
        
        .markdown-content code {
            background-color: rgba(0, 0, 0, 0.05);
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 0.9em;
            font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
        }
        
        .markdown-content pre {
            background: linear-gradient(135deg, var(--code-bg) 0%, #253b5c 100%);
            color: var(--code-color);
            padding: 16px;
            border-radius: 8px;
            overflow-x: auto;
            margin: 1em 0;
            font-size: 0.9em;
            line-height: 1.5;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
            position: relative;
        }
        
        .markdown-content pre code {
            background-color: transparent;
            padding: 0;
            border-radius: 0;
            font-size: 1em;
            color: inherit;
        }
        
        .markdown-content table {
            border-collapse: collapse;
            width: 100%;
            margin-bottom: 1em;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
            border-radius: 8px;
            overflow: hidden;
            border: 1px solid var(--border);
        }
        
        .markdown-content th, 
        .markdown-content td {
            border: 1px solid var(--border);
            padding: 12px 16px;
            text-align: left;
        }
        
        .markdown-content th {
            background: linear-gradient(135deg, var(--light-bg) 0%, #e8e8e8 100%);
            font-weight: 600;
        }
        
        .markdown-content tr:nth-child(even) {
            background-color: rgba(0, 0, 0, 0.02);
        }
        
        .markdown-content a {
            color: var(--primary);
            text-decoration: none;
            border-bottom: 1px solid transparent;
            transition: all 0.2s;
        }
        
        .markdown-content a:hover {
            border-bottom: 1px solid var(--primary);
        }
        
        .markdown-content img {
            max-width: 100%;
            height: auto;
            border-radius: 8px;
            margin: 1em 0;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }
        
        .markdown-content hr {
            border: none;
            border-top: 1px solid var(--border);
            margin: 2em 0;
        }
        
        .markdown-content strong, .markdown-content b {
            font-weight: 600;
            color: var(--text);
        }
        
        .markdown-content em, .markdown-content i {
            font-style: italic;
        }
        
        /* 代码块语言标签 */
        .code-language {
            display: inline-block;
            background: linear-gradient(135deg, #34495e 0%, #2c3e50 100%);
            color: #bdc3c7;
            padding: 6px 12px;
            border-radius: 6px 6px 0 0;
            font-size: 12px;
            margin-bottom: -10px;
            position: relative;
            z-index: 1;
            font-weight: 600;
        }
        
        /* KaTeX LaTeX样式 */
        .katex {
            font-size: 1.1em !important;
        }
        
        .katex-display {
            overflow-x: auto;
            overflow-y: hidden;
            padding: 1em 0;
            margin: 1em 0;
            text-align: center;
        }
        
        .katex-display > .katex {
            white-space: nowrap;
        }
        
        /* 数学公式特殊样式 */
        .katex .mfrac {
            padding: 0 0.2em;
        }
        
        /* 流式渲染进度指示器 */
        .streaming-indicator {
            display: inline-block;
            width: 6px;
            height: 6px;
            background-color: var(--primary);
            border-radius: 50%;
            margin-left: 4px;
            animation: pulse 1.5s infinite;
            vertical-align: middle;
        }
        
        @keyframes pulse {
            0%, 100% { opacity: 0.5; }
            50% { opacity: 1; }
        }
        
        /* 费用显示 */
        .cost-display {
            display: flex;
            justify-content: space-between;
            background-color: var(--light-bg);
            padding: 12px 16px;
            border-radius: 8px;
            margin-top: 15px;
            font-size: 13px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
        }
    </style>
</head>
<body>
    <div class="app-container">
        <!-- 侧边栏 -->
        <div class="sidebar">
            <div class="logo">
                <h1>DeepSeek</h1>
                <div class="version">Web客户端 v1.6</div>
            </div>
            
            <div class="menu">
                <div class="menu-section">
                    <h3>工具</h3>
                    <div class="menu-item" id="settingsBtn">
                        <i class="fas fa-cog"></i>
                        <span>设置</span>
                    </div>
                </div>
                
                <div class="menu-section">
                    <h3>对话管理</h3>
                    <div class="menu-item" id="newChatBtn">
                        <i class="fas fa-plus-circle"></i>
                        <span>新对话</span>
                    </div>
                    <div class="menu-item" id="saveChatBtn">
                        <i class="fas fa-save"></i>
                        <span>保存对话</span>
                    </div>
                    <div class="menu-item" id="loadChatBtn">
                        <i class="fas fa-folder-open"></i>
                        <span>加载对话</span>
                    </div>
                </div>
            </div>
            
            <div class="status-bar">
                <div class="status-item">
                    <span>总费用:</span>
                    <span class="status-value" id="totalCost">0.00 元</span>
                </div>
                <div class="status-item">
                    <span>总Tokens:</span>
                    <span class="status-value" id="totalTokens">0</span>
                </div>
                <div class="status-item">
                    <span>模型:</span>
                    <span class="status-value" id="currentModel">deepseek-chat</span>
                </div>
            </div>
        </div>
        
        <!-- 主内容区 -->
        <div class="main-content">
            <div class="header">
                <div class="chat-title">DeepSeek对话</div>
                <div class="header-actions">
                    <button class="header-btn" id="showParamsBtn">
                        <i class="fas fa-sliders-h"></i>
                        设置
                    </button>
                    <button class="header-btn" id="clearChatBtn">
                        <i class="fas fa-trash-alt"></i>
                        清空
                    </button>
                </div>
            </div>
            
            <div class="chat-container">
                <div class="messages-container" id="messagesContainer">
                    <div class="empty-chat" id="emptyChat">
                        <i class="fas fa-robot"></i>
                        <h3>欢迎使用DeepSeek Web客户端</h3>
                        <p>这是一个功能完整的DeepSeek API客户端,支持流式响应、参数调节、费用计算等功能。</p>
                        <p style="margin-top: 20px;">请在侧边栏设置中配置您的API密钥以开始使用。</p>
                    </div>
                </div>
                
                <div class="input-container">
                    <div class="input-area">
                        <textarea class="message-input" id="messageInput" placeholder="输入消息,Enter换行,Ctrl+Enter发送" rows="3"></textarea>
                        <button class="send-button" id="sendButton" disabled>
                            <i class="fas fa-paper-plane"></i>
                            发送
                        </button>
                    </div>
                    <div class="input-actions">
                        <button class="action-btn" id="toggleReasoningBtn" title="深度思考模式">
                            <i class="fas fa-brain"></i>
                            深度思考: <span id="reasoningStatus">关闭</span>
                        </button>
                        <button class="action-btn" id="toggleAutoRenderBtn" title="自动Markdown渲染">
                            <i class="fas fa-code"></i>
                            Markdown渲染: <span id="renderStatus">开启</span>
                        </button>
                        <button class="action-btn" id="toggleLatexBtn" title="LaTeX公式渲染">
                            <i class="fas fa-square-root-alt"></i>
                            LaTeX渲染: <span id="latexStatus">开启</span>
                        </button>
                    </div>
                    <div class="input-hint">提示:输入消息后按Ctrl+Enter发送,Enter键换行</div>
                </div>
            </div>
        </div>
        
        <!-- 设置面板 -->
        <div class="settings-panel" id="settingsPanel">
            <div class="settings-header">
                <div class="settings-title">DeepSeek设置</div>
                <div class="settings-subtitle">配置API密钥和模型参数</div>
            </div>
            
            <div class="settings-content">
                <div class="alert alert-info">
                    <i class="fas fa-info-circle"></i>
                    请从DeepSeek官网获取API密钥并在此配置
                </div>
                
                <div class="settings-group">
                    <div class="settings-group-title">
                        <i class="fas fa-key"></i>
                        API配置
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">API密钥</label>
                        <input type="password" class="setting-control" id="apiKey" placeholder="输入DeepSeek API密钥">
                        <div class="setting-description">
                            密钥长度应为35个字符。您可以在DeepSeek平台获取API密钥。
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">模型选择</label>
                        <select class="setting-control" id="modelSelect">
                            <option value="deepseek-chat">deepseek-chat (标准)</option>
                            <option value="deepseek-reasoner">deepseek-reasoner (深度思考)</option>
                        </select>
                        <div class="setting-description">
                            深度思考模型提供更详细的推理过程,但价格更高。
                        </div>
                    </div>
                </div>
                
                <div class="settings-group">
                    <div class="settings-group-title">
                        <i class="fas fa-sliders-h"></i>
                        生成参数
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">温度 (Temperature)</label>
                        <div class="slider-container">
                            <input type="range" class="setting-control" id="temperature" min="0" max="2" step="0.1" value="0.7">
                            <span class="slider-value" id="temperatureValue">0.7</span>
                        </div>
                        <div class="setting-description">
                            控制输出的随机性。值越高,输出越随机;值越低,输出越确定。
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">核采样 (Top P)</label>
                        <div class="slider-container">
                            <input type="range" class="setting-control" id="topP" min="0" max="1" step="0.05" value="1">
                            <span class="slider-value" id="topPValue">1</span>
                        </div>
                        <div class="setting-description">
                            控制核采样方法的概率阈值。通常与温度参数一起使用。
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">频率惩罚 (Frequency Penalty)</label>
                        <div class="slider-container">
                            <input type="range" class="setting-control" id="frequencyPenalty" min="-2" max="2" step="0.1" value="0">
                            <span class="slider-value" id="frequencyPenaltyValue">0</span>
                        </div>
                        <div class="setting-description">
                            正值减少重复token的出现,负值增加重复token的出现。
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">存在惩罚 (Presence Penalty)</label>
                        <div class="slider-container">
                            <input type="range" class="setting-control" id="presencePenalty" min="-2" max="2" step="0.1" value="0">
                            <span class="slider-value" id="presencePenaltyValue">0</span>
                        </div>
                        <div class="setting-description">
                            正值鼓励模型谈论新话题,负值鼓励模型重复已提及的话题。
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">最大生成长度 (Max Tokens)</label>
                        <div class="slider-container">
                            <input type="range" class="setting-control" id="maxTokens" min="100" max="4096" step="100" value="2048">
                            <span class="slider-value" id="maxTokensValue">2048</span>
                        </div>
                        <div class="setting-description">
                            控制API响应的最大长度。注意:上下文总长度有限制。
                        </div>
                    </div>
                </div>
                
                <!-- 系统消息设置 -->
                <div class="settings-group">
                    <div class="settings-group-title">
                        <i class="fas fa-comment"></i>
                        系统消息
                    </div>
                    
                    <div class="setting-item">
                        <label class="setting-label">系统消息(可选)</label>
                        <textarea class="setting-control" id="systemPrompt" rows="4" placeholder="输入系统消息,用于设置AI的行为背景(例如:你是一个有用的助手)"></textarea>
                        <div class="setting-description">
                            系统消息将在每次对话开始时作为背景设定发送给AI。
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="settings-footer">
                <button class="settings-btn" id="saveSettingsBtn">
                    保存设置
                </button>
                <button class="settings-btn secondary" id="closeSettingsBtn">
                    关闭
                </button>
            </div>
        </div>
    </div>

    <!-- KaTeX JS for LaTeX rendering -->
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js" crossorigin="anonymous"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js" crossorigin="anonymous"></script>
    
    <script>
        // DeepSeek Web客户端
        class DeepSeekClient {
            constructor() {
                // 状态变量
                this.isDebugMode = false;
                this.useReasoningModel = false;
                this.autoRender = true;
                this.enableLatex = true;
                this.totalCost = 0;
                this.totalTokens = 0;
                this.isLoading = false;
                this.isStreaming = false;
                this.systemPrompt = '';
                this.hasSystemMessageInConversation = false;
                
                // 当前对话
                this.currentConversation = {
                    id: this.generateId(),
                    title: '新对话',
                    messages: [],
                    timestamp: Date.now(),
                    usage: {
                        prompt_cache_hit_tokens: 0,
                        prompt_cache_miss_tokens: 0,
                        completion_tokens: 0,
                        total_tokens: 0
                    }
                };
                
                // 对话历史
                this.conversations = [];
                
                // 参数默认值
                this.params = {
                    temperature: 0.7,
                    topP: 1,
                    frequencyPenalty: 0,
                    presencePenalty: 0,
                    maxTokens: 2048,
                    model: 'deepseek-chat'
                };
                
                // API价格(元/百万token)
                this.prices = {
                    cacheHit: 0.2,
                    cacheMiss: 2,
                    output: 3
                };
                
                // 初始化
                this.init();
            }
            
            // 初始化应用
            init() {
                this.loadSettings();
                this.loadConversations();
                this.setupEventListeners();
                this.updateUI();
                
                console.log('DeepSeek Web客户端已初始化');
            }
            
            // 生成唯一ID
            generateId() {
                return Date.now().toString(36) + Math.random().toString(36).substr(2);
            }
            
            // 加载设置
            loadSettings() {
                const settings = localStorage.getItem('deepseek_settings');
                if (settings) {
                    try {
                        const parsed = JSON.parse(settings);
                        this.params = { ...this.params, ...parsed.params };
                        this.totalCost = parsed.totalCost || 0;
                        this.totalTokens = parsed.totalTokens || 0;
                        this.autoRender = parsed.autoRender !== undefined ? parsed.autoRender : true;
                        this.enableLatex = parsed.enableLatex !== undefined ? parsed.enableLatex : true;
                        this.systemPrompt = parsed.systemPrompt || '';
                        
                        // 更新UI元素
                        document.getElementById('apiKey').value = parsed.apiKey || '';
                        document.getElementById('modelSelect').value = this.params.model;
                        document.getElementById('systemPrompt').value = this.systemPrompt;
                        document.getElementById('temperature').value = this.params.temperature;
                        document.getElementById('temperatureValue').textContent = this.params.temperature;
                        document.getElementById('topP').value = this.params.topP;
                        document.getElementById('topPValue').textContent = this.params.topP;
                        document.getElementById('frequencyPenalty').value = this.params.frequencyPenalty;
                        document.getElementById('frequencyPenaltyValue').textContent = this.params.frequencyPenalty;
                        document.getElementById('presencePenalty').value = this.params.presencePenalty;
                        document.getElementById('presencePenaltyValue').textContent = this.params.presencePenalty;
                        document.getElementById('maxTokens').value = this.params.maxTokens;
                        document.getElementById('maxTokensValue').textContent = this.params.maxTokens;
                        
                        // 更新状态栏
                        document.getElementById('totalCost').textContent = this.totalCost.toFixed(6) + ' 元';
                        document.getElementById('totalTokens').textContent = this.totalTokens;
                        document.getElementById('currentModel').textContent = this.params.model;
                        
                        // 更新按钮状态
                        document.getElementById('renderStatus').textContent = this.autoRender ? '开启' : '关闭';
                        document.getElementById('reasoningStatus').textContent = this.useReasoningModel ? '开启' : '关闭';
                        document.getElementById('latexStatus').textContent = this.enableLatex ? '开启' : '关闭';
                        
                        // 启用发送按钮(如果有API密钥)
                        if (parsed.apiKey && parsed.apiKey.length === 35) {
                            document.getElementById('sendButton').disabled = false;
                        }
                    } catch (e) {
                        console.error('加载设置时出错:', e);
                    }
                }
            }
            
            // 保存设置
            saveSettings() {
                const settings = {
                    apiKey: document.getElementById('apiKey').value,
                    params: this.params,
                    totalCost: this.totalCost,
                    totalTokens: this.totalTokens,
                    autoRender: this.autoRender,
                    enableLatex: this.enableLatex,
                    systemPrompt: this.systemPrompt
                };
                
                localStorage.setItem('deepseek_settings', JSON.stringify(settings));
                
                // 如果API密钥有效,启用发送按钮
                if (settings.apiKey && settings.apiKey.length === 35) {
                    document.getElementById('sendButton').disabled = false;
                } else {
                    document.getElementById('sendButton').disabled = true;
                }
                
                this.showAlert('设置已保存', 'success');
            }
            
            // 加载对话历史
            loadConversations() {
                const conversations = localStorage.getItem('deepseek_conversations');
                if (conversations) {
                    try {
                        this.conversations = JSON.parse(conversations);
                    } catch (e) {
                        console.error('加载对话历史时出错:', e);
                    }
                }
            }
            
            // 保存对话历史
            saveConversations() {
                localStorage.setItem('deepseek_conversations', JSON.stringify(this.conversations));
            }
            
            // 设置事件监听器
            setupEventListeners() {
                // 发送消息
                document.getElementById('sendButton').addEventListener('click', () => this.sendMessage());
                
                // 输入框快捷键:Ctrl+Enter发送,Enter换行
                document.getElementById('messageInput').addEventListener('keydown', (e) => {
                    if (e.key === 'Enter' && e.ctrlKey) {
                        e.preventDefault();
                        this.sendMessage();
                    }
                });
                
                // 输入框自动调整高度
                document.getElementById('messageInput').addEventListener('input', function() {
                    this.style.height = 'auto';
                    this.style.height = Math.min(this.scrollHeight, 200) + 'px';
                });
                
                // 切换设置面板
                document.getElementById('showParamsBtn').addEventListener('click', () => {
                    document.getElementById('settingsPanel').classList.toggle('open');
                });
                
                document.getElementById('settingsBtn').addEventListener('click', () => {
                    document.getElementById('settingsPanel').classList.toggle('open');
                });
                
                // 关闭设置面板
                document.getElementById('closeSettingsBtn').addEventListener('click', () => {
                    document.getElementById('settingsPanel').classList.remove('open');
                });
                
                // 保存设置
                document.getElementById('saveSettingsBtn').addEventListener('click', () => {
                    this.updateParamsFromUI();
                    this.systemPrompt = document.getElementById('systemPrompt').value;
                    this.saveSettings();
                    document.getElementById('settingsPanel').classList.remove('open');
                });
                
                // 清空对话
                document.getElementById('clearChatBtn').addEventListener('click', () => {
                    if (confirm('确定要清空当前对话吗?')) {
                        this.newConversation();
                    }
                });
                
                // 新对话
                document.getElementById('newChatBtn').addEventListener('click', () => {
                    this.newConversation();
                });
                
                // 保存对话
                document.getElementById('saveChatBtn').addEventListener('click', () => {
                    this.saveConversationToFile();
                });
                
                // 加载对话
                document.getElementById('loadChatBtn').addEventListener('click', () => {
                    this.loadConversationFromFile();
                });
                
                // 切换深度思考模式
                document.getElementById('toggleReasoningBtn').addEventListener('click', () => {
                    this.toggleReasoningModel();
                });
                
                // 切换Markdown渲染
                document.getElementById('toggleAutoRenderBtn').addEventListener('click', () => {
                    this.toggleAutoRender();
                });
                
                // 切换LaTeX渲染
                document.getElementById('toggleLatexBtn').addEventListener('click', () => {
                    this.toggleLatex();
                });
                
                // 参数滑块事件
                this.setupSliderEvents();
            }
            
            // 设置滑块事件
            setupSliderEvents() {
                const sliders = [
                    { id: 'temperature', valueId: 'temperatureValue' },
                    { id: 'topP', valueId: 'topPValue' },
                    { id: 'frequencyPenalty', valueId: 'frequencyPenaltyValue' },
                    { id: 'presencePenalty', valueId: 'presencePenaltyValue' },
                    { id: 'maxTokens', valueId: 'maxTokensValue' }
                ];
                
                sliders.forEach(slider => {
                    const element = document.getElementById(slider.id);
                    const valueElement = document.getElementById(slider.valueId);
                    
                    element.addEventListener('input', () => {
                        valueElement.textContent = element.value;
                    });
                });
            }
            
            // 从UI更新参数
            updateParamsFromUI() {
                this.params.temperature = parseFloat(document.getElementById('temperature').value);
                this.params.topP = parseFloat(document.getElementById('topP').value);
                this.params.frequencyPenalty = parseFloat(document.getElementById('frequencyPenalty').value);
                this.params.presencePenalty = parseFloat(document.getElementById('presencePenalty').value);
                this.params.maxTokens = parseInt(document.getElementById('maxTokens').value);
                this.params.model = document.getElementById('modelSelect').value;
                
                // 更新状态栏
                document.getElementById('currentModel').textContent = this.params.model;
                
                console.log('参数已更新:', this.params);
            }
            
            // ==================== 新增:编辑与重新生成功能 ====================
            
            // 为消息元素添加编辑和重新生成按钮
            enhanceMessageElement(element, role, messageId) {
                const header = element.querySelector('.message-header');
                if (!header) return;
                
                // 创建按钮容器
                const actionsDiv = document.createElement('div');
                actionsDiv.className = 'message-actions';
                
                // 编辑按钮(所有消息类型都添加)
                const editBtn = document.createElement('button');
                editBtn.className = 'message-action-btn';
                editBtn.title = '编辑消息';
                editBtn.innerHTML = '<i class="fas fa-edit"></i>';
                editBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.enterEditMode(element, messageId, role);
                });
                actionsDiv.appendChild(editBtn);
                
                // 重新生成按钮(仅AI消息)
                if (role === 'assistant') {
                    const regenBtn = document.createElement('button');
                    regenBtn.className = 'message-action-btn';
                    regenBtn.title = '重新生成';
                    regenBtn.innerHTML = '<i class="fas fa-sync-alt"></i>';
                    regenBtn.addEventListener('click', (e) => {
                        e.stopPropagation();
                        this.regenerateMessage(messageId);
                    });
                    actionsDiv.appendChild(regenBtn);
                }
                
                header.appendChild(actionsDiv);
                
                // 为AI头像添加重新生成功能
                if (role === 'assistant') {
                    const avatar = element.querySelector('.message-avatar');
                    if (avatar) {
                        avatar.addEventListener('click', (e) => {
                            e.stopPropagation();
                            this.regenerateMessage(messageId);
                        });
                    }
                }
            }
            
            // 进入编辑模式
            enterEditMode(messageElement, messageId, role) {
                const contentElement = messageElement.querySelector('.message-content');
                if (!contentElement) return;
                
                const originalContent = contentElement.dataset.originalText || contentElement.textContent;
                
                // 保存原始内容以备取消
                contentElement.dataset.originalContent = originalContent;
                
                // 替换为编辑界面
                const textarea = document.createElement('textarea');
                textarea.className = 'edit-textarea';
                textarea.value = originalContent;
                textarea.rows = Math.min(10, originalContent.split('\n').length + 2);
                
                const saveBtn = document.createElement('button');
                saveBtn.className = 'edit-btn edit-save';
                saveBtn.textContent = '保存';
                saveBtn.addEventListener('click', () => {
                    this.saveEdit(messageElement, messageId, textarea.value, role);
                });
                
                const cancelBtn = document.createElement('button');
                cancelBtn.className = 'edit-btn edit-cancel';
                cancelBtn.textContent = '取消';
                cancelBtn.addEventListener('click', () => {
                    this.cancelEdit(messageElement, originalContent);
                });
                
                const editActions = document.createElement('div');
                editActions.className = 'edit-actions';
                editActions.appendChild(saveBtn);
                editActions.appendChild(cancelBtn);
                
                // 清空内容区域并添加编辑控件
                contentElement.innerHTML = '';
                contentElement.appendChild(textarea);
                contentElement.appendChild(editActions);
            }
            
            // 保存编辑
            saveEdit(messageElement, messageId, newContent, role) {
                // 更新数组中的消息内容
                const msgIndex = this.currentConversation.messages.findIndex(m => m.messageId === messageId);
                if (msgIndex === -1) return;
                
                const originalMsg = this.currentConversation.messages[msgIndex];
                if (originalMsg.content === newContent) {
                    // 内容未变,直接退出编辑
                    this.cancelEdit(messageElement, newContent);
                    return;
                }
                
                // 更新消息内容
                this.currentConversation.messages[msgIndex].content = newContent;
                
                // 删除该消息之后的所有消息(因为历史改变了)
                this.deleteMessagesAfter(messageId);
                
                // 重新渲染该消息的内容
                const contentElement = messageElement.querySelector('.message-content');
                contentElement.innerHTML = ''; // 移除编辑控件
                if (role === 'assistant' && this.autoRender) {
                    this.renderAndSetContent(contentElement, newContent);
                } else {
                    contentElement.textContent = newContent;
                    if (role === 'assistant') contentElement.classList.add('raw-text');
                }
                
                // 滚动到底部
                this.scrollToBottom();
                this.showAlert('消息已更新', 'success');
                
                // 根据角色自动触发后续回复
                if (role === 'user') {
                    // 编辑的是用户消息,从该消息继续生成AI回复
                    this.continueFromMessage(messageId);
                } else if (role === 'assistant') {
                    // 编辑的是AI消息,重新生成该消息
                    this.regenerateMessage(messageId);
                }
            }
            
            // 取消编辑
            cancelEdit(messageElement, originalContent) {
                const contentElement = messageElement.querySelector('.message-content');
                contentElement.innerHTML = ''; // 移除编辑控件
                contentElement.textContent = originalContent;
                // 恢复可能的渲染类
                if (messageElement.classList.contains('assistant-message') && this.autoRender) {
                    this.renderAndSetContent(contentElement, originalContent);
                }
            }
            
            // 删除某条消息之后的所有消息(从数组和DOM)
            deleteMessagesAfter(messageId) {
                const startIndex = this.currentConversation.messages.findIndex(m => m.messageId === messageId);
                if (startIndex === -1) return;
                
                // 获取所有后续消息的ID
                const afterMessages = this.currentConversation.messages.slice(startIndex + 1);
                const afterIds = afterMessages.map(m => m.messageId);
                
                // 从DOM中移除这些消息元素
                afterIds.forEach(id => {
                    const elem = document.querySelector(`.message[data-message-id="${id}"]`);
                    if (elem) elem.remove();
                });
                
                // 从数组中截断
                this.currentConversation.messages = this.currentConversation.messages.slice(0, startIndex + 1);
            }
            
            // 重新生成AI消息
            async regenerateMessage(messageId) {
                const index = this.currentConversation.messages.findIndex(m => m.messageId === messageId);
                if (index === -1 || this.currentConversation.messages[index].role !== 'assistant') return;
                
                // 检查API密钥
                const apiKey = document.getElementById('apiKey').value;
                if (!apiKey || apiKey.length !== 35) {
                    this.showAlert('请先配置有效的API密钥', 'danger');
                    return;
                }
                
                // 禁用发送按钮
                this.isLoading = true;
                this.isStreaming = true;
                document.getElementById('sendButton').disabled = true;
                
                // 删除该消息之后的所有消息
                this.deleteMessagesAfter(messageId);
                
                // 获取消息元素
                const messageElement = document.querySelector(`.message[data-message-id="${messageId}"]`);
                if (!messageElement) return;
                
                const contentElement = messageElement.querySelector('.message-content');
                if (!contentElement) return;
                
                // 清空当前内容,显示加载指示器
                contentElement.innerHTML = '';
                const loadingDiv = document.createElement('div');
                loadingDiv.className = 'typing-indicator';
                loadingDiv.innerHTML = '重新生成中 <div class="typing-dots"><span></span><span></span><span></span></div>';
                contentElement.appendChild(loadingDiv);
                
                // 准备上下文消息(该消息之前的所有消息)
                const contextMessages = this.currentConversation.messages.slice(0, index); // 不包含当前消息
                
                try {
                    // 调用流式生成,更新当前元素
                    await this.streamToElement(messageElement, contextMessages, {
                        onFinish: (usage) => {
                            if (usage) {
                                this.updateUsage(usage);
                            }
                            // 重新启用发送按钮
                            this.isLoading = false;
                            this.isStreaming = false;
                            document.getElementById('sendButton').disabled = false;
                        }
                    });
                } catch (error) {
                    console.error('重新生成失败:', error);
                    this.showAlert('重新生成失败: ' + error.message, 'danger');
                    // 恢复原内容?简单显示错误
                    contentElement.innerHTML = '生成失败,请重试。';
                    this.isLoading = false;
                    this.isStreaming = false;
                    document.getElementById('sendButton').disabled = false;
                }
            }
            
            // 从某条用户消息继续生成(编辑用户消息后调用)
            async continueFromMessage(messageId) {
                const index = this.currentConversation.messages.findIndex(m => m.messageId === messageId);
                if (index === -1) return;
                const userMsg = this.currentConversation.messages[index];
                if (userMsg.role !== 'user') return;
                
                // 检查API密钥
                const apiKey = document.getElementById('apiKey').value;
                if (!apiKey || apiKey.length !== 35) {
                    this.showAlert('请先配置有效的API密钥', 'danger');
                    return;
                }
                
                this.isLoading = true;
                this.isStreaming = true;
                document.getElementById('sendButton').disabled = true;
                
                // 创建新的AI消息元素
                const aiMsgId = this.generateId();
                const aiMessageElement = this.createMessageElement('', 'assistant', aiMsgId);
                document.getElementById('messagesContainer').appendChild(aiMessageElement);
                this.scrollToBottom();
                
                // 上下文:包括该用户消息及之前的所有消息
                const contextMessages = this.currentConversation.messages.slice(0, index + 1);
                
                try {
                    await this.streamToElement(aiMessageElement, contextMessages, {
                        onFinish: (usage) => {
                            if (usage) this.updateUsage(usage);
                            // 将生成的AI消息加入数组
                            const content = aiMessageElement.querySelector('.message-content').dataset.originalText || '';
                            this.currentConversation.messages.push({
                                role: 'assistant',
                                content: content,
                                reasoning: aiMessageElement.querySelector('.reasoning-content')?.textContent || '',
                                timestamp: Date.now(),
                                usage: usage,
                                messageId: aiMsgId
                            });
                            this.isLoading = false;
                            this.isStreaming = false;
                            document.getElementById('sendButton').disabled = false;
                        }
                    });
                } catch (error) {
                    console.error('继续生成失败:', error);
                    this.showAlert('继续生成失败: ' + error.message, 'danger');
                    aiMessageElement.remove();
                    this.isLoading = false;
                    this.isStreaming = false;
                    document.getElementById('sendButton').disabled = false;
                }
            }
            
            // 通用的流式生成方法,将结果渲染到指定的消息元素中(元素已存在)
            async streamToElement(messageElement, contextMessages, options = {}) {
                const apiKey = document.getElementById('apiKey').value;
                const contentElement = messageElement.querySelector('.message-content');
                if (!contentElement) throw new Error('消息内容元素不存在');
                
                // 准备请求体
                const requestBody = {
                    model: this.params.model,
                    messages: this.prepareMessagesFromContext(contextMessages),
                    temperature: this.params.temperature,
                    top_p: this.params.topP,
                    frequency_penalty: this.params.frequencyPenalty,
                    presence_penalty: this.params.presencePenalty,
                    max_tokens: this.params.maxTokens,
                    stream: true
                };
                
                if (this.isDebugMode) {
                    console.log('API请求:', requestBody);
                }
                
                const response = await fetch('https://api.deepseek.com/chat/completions', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${apiKey}`
                    },
                    body: JSON.stringify(requestBody)
                });
                
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
                
                const reader = response.body.getReader();
                const decoder = new TextDecoder();
                let fullResponse = '';
                let reasoningContent = '';
                let isReasoning = false;
                let usageData = null;
                
                let lastRenderTime = 0;
                const renderInterval = 50;
                
                // 清空contentElement,准备接收流式内容
                contentElement.innerHTML = '';
                
                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;
                    
                    const chunk = decoder.decode(value);
                    const lines = chunk.split('\n').filter(line => line.trim() !== '');
                    
                    for (const line of lines) {
                        if (line.startsWith('data: ')) {
                            const data = line.substring(6);
                            if (data === '[DONE]') break;
                            
                            try {
                                const parsed = JSON.parse(data);
                                
                                if (parsed.usage) {
                                    usageData = parsed.usage;
                                }
                                
                                if (parsed.choices && parsed.choices[0].delta.reasoning_content) {
                                    isReasoning = true;
                                    reasoningContent += parsed.choices[0].delta.reasoning_content;
                                    this.updateReasoningDisplay(messageElement, reasoningContent);
                                }
                                
                                if (parsed.choices && parsed.choices[0].delta.content) {
                                    if (isReasoning) {
                                        isReasoning = false;
                                        fullResponse += '\n\n';
                                    }
                                    fullResponse += parsed.choices[0].delta.content;
                                    
                                    const now = Date.now();
                                    if (now - lastRenderTime > renderInterval) {
                                        this.updateMessageContentStreaming(messageElement, fullResponse, reasoningContent);
                                        lastRenderTime = now;
                                    }
                                    this.scrollToBottom();
                                }
                            } catch (e) {
                                console.error('解析流数据失败:', e);
                            }
                        }
                    }
                }
                
                // 最终更新
                this.finalRenderMessage(messageElement, fullResponse, reasoningContent);
                
                // 更新数组中的消息内容(messageId对应的消息)
                const messageId = messageElement.dataset.messageId;
                const msgIndex = this.currentConversation.messages.findIndex(m => m.messageId === messageId);
                if (msgIndex !== -1) {
                    this.currentConversation.messages[msgIndex].content = fullResponse;
                    this.currentConversation.messages[msgIndex].reasoning = reasoningContent;
                    this.currentConversation.messages[msgIndex].usage = usageData;
                }
                
                if (options.onFinish) options.onFinish(usageData);
            }
            
            // 根据上下文消息数组准备API messages(处理系统消息)
            prepareMessagesFromContext(contextMessages) {
                const messages = [];
                if (this.systemPrompt && !this.hasSystemMessageInConversation) {
                    messages.push({ role: 'system', content: this.systemPrompt });
                }
                contextMessages.forEach(msg => {
                    messages.push({ role: msg.role, content: msg.content });
                });
                return messages;
            }
            
            // 发送消息(修改,使用streamToElement)
            async sendMessage() {
                const input = document.getElementById('messageInput');
                const message = input.value.trim();
                
                if (!message) {
                    this.showAlert('请输入消息', 'warning');
                    return;
                }
                
                const apiKey = document.getElementById('apiKey').value;
                if (!apiKey || apiKey.length !== 35) {
                    this.showAlert('请先配置有效的API密钥', 'danger');
                    document.getElementById('settingsPanel').classList.add('open');
                    return;
                }
                
                this.isLoading = true;
                this.isStreaming = true;
                document.getElementById('sendButton').disabled = true;
                
                const emptyChat = document.getElementById('emptyChat');
                if (emptyChat) emptyChat.style.display = 'none';
                
                if (this.currentConversation.messages.length === 0 && this.systemPrompt) {
                    this.addSystemMessage(this.systemPrompt);
                    this.hasSystemMessageInConversation = true;
                }
                
                // 添加用户消息
                const userMsgId = this.generateId();
                this.addMessageToUI(message, 'user', userMsgId);
                this.currentConversation.messages.push({
                    role: 'user',
                    content: message,
                    timestamp: Date.now(),
                    messageId: userMsgId
                });
                
                input.value = '';
                input.style.height = 'auto';
                
                // 创建AI消息元素(但先不加入内容)
                const aiMsgId = this.generateId();
                const aiMessageElement = this.createMessageElement('', 'assistant', aiMsgId);
                document.getElementById('messagesContainer').appendChild(aiMessageElement);
                this.scrollToBottom();
                
                // 准备上下文(包括刚添加的用户消息)
                const contextMessages = this.currentConversation.messages.slice(); // 包含刚添加的用户消息
                
                try {
                    await this.streamToElement(aiMessageElement, contextMessages, {
                        onFinish: (usage) => {
                            if (usage) {
                                this.updateUsage(usage);
                            }
                            // 将AI消息加入数组
                            const content = aiMessageElement.querySelector('.message-content').dataset.originalText || '';
                            this.currentConversation.messages.push({
                                role: 'assistant',
                                content: content,
                                reasoning: aiMessageElement.querySelector('.reasoning-content')?.textContent || '',
                                timestamp: Date.now(),
                                usage: usage,
                                messageId: aiMsgId
                            });
                            
                            this.isLoading = false;
                            this.isStreaming = false;
                            document.getElementById('sendButton').disabled = false;
                        }
                    });
                } catch (error) {
                    console.error('API调用失败:', error);
                    this.showAlert('API调用失败: ' + error.message, 'danger');
                    aiMessageElement.remove();
                    this.isLoading = false;
                    this.isStreaming = false;
                    document.getElementById('sendButton').disabled = false;
                }
            }
            
            // ==================== 原有方法,需要适当修改 ====================
            
            // 添加消息到UI(增加messageId参数)
            addMessageToUI(content, role, messageId = null) {
                if (!messageId) messageId = this.generateId();
                const messageElement = this.createMessageElement(content, role, messageId);
                document.getElementById('messagesContainer').appendChild(messageElement);
                
                // 添加编辑/重新生成按钮
                this.enhanceMessageElement(messageElement, role, messageId);
                
                if (role === 'assistant' && this.autoRender) {
                    this.renderAndSetContent(messageElement.querySelector('.message-content'), content);
                } else if (role === 'assistant' && !this.autoRender) {
                    const contentElement = messageElement.querySelector('.message-content');
                    contentElement.textContent = content;
                    contentElement.classList.add('raw-text');
                } else if (role === 'system') {
                    const contentElement = messageElement.querySelector('.message-content');
                    contentElement.textContent = content;
                }
                
                this.scrollToBottom();
                
                const emptyChat = document.getElementById('emptyChat');
                if (emptyChat) emptyChat.style.display = 'none';
            }
            
            // 创建消息元素(增加messageId)
            createMessageElement(content, role, messageId) {
                const element = document.createElement('div');
                element.className = `message ${role}-message`;
                element.dataset.messageId = messageId;
                
                const senderName = role === 'user' ? '用户' : (role === 'assistant' ? 'AI助手' : '系统');
                const avatarClass = role === 'user' ? 'user-avatar' : (role === 'assistant' ? 'assistant-avatar' : 'system-avatar');
                const senderClass = role === 'user' ? 'user-sender' : (role === 'assistant' ? 'assistant-sender' : 'system-sender');
                
                const contentDiv = document.createElement('div');
                contentDiv.className = `message-content ${role}-content`;
                contentDiv.textContent = content;
                
                element.innerHTML = `
                    <div class="message-header">
                        <div class="message-avatar ${avatarClass}">${role === 'user' ? '你' : (role === 'assistant' ? 'AI' : '系')}</div>
                        <div class="message-sender ${senderClass}">${senderName}</div>
                        <div class="message-time">${this.formatTime(new Date())}</div>
                    </div>
                `;
                
                element.appendChild(contentDiv);
                
                return element;
            }
            
            // 更新推理内容显示
            updateReasoningDisplay(messageElement, reasoningContent) {
                let reasoningElement = messageElement.querySelector('.reasoning-content');
                
                if (!reasoningElement) {
                    if (!reasoningContent) return;
                    reasoningElement = document.createElement('div');
                    reasoningElement.className = 'reasoning-content';
                    const contentElement = messageElement.querySelector('.message-content');
                    messageElement.insertBefore(reasoningElement, contentElement);
                }
                
                if (reasoningContent) {
                    reasoningElement.textContent = reasoningContent;
                } else {
                    reasoningElement.remove();
                }
            }
            
            // 流式更新消息内容(针对元素)
            updateMessageContentStreaming(messageElement, content, reasoningContent) {
                const contentElement = messageElement.querySelector('.message-content');
                
                if (!this.autoRender) {
                    contentElement.textContent = content;
                    contentElement.classList.add('raw-text');
                } else {
                    contentElement.classList.remove('raw-text');
                    contentElement.textContent = content;
                }
                
                this.addStreamingIndicator(contentElement);
            }
            
            // 添加流式传输指示器
            addStreamingIndicator(contentElement) {
                let indicator = contentElement.querySelector('.streaming-indicator');
                if (!indicator) {
                    indicator = document.createElement('span');
                    indicator.className = 'streaming-indicator';
                    contentElement.appendChild(indicator);
                }
            }
            
            // 移除流式传输指示器
            removeStreamingIndicator(contentElement) {
                const indicator = contentElement.querySelector('.streaming-indicator');
                if (indicator) indicator.remove();
            }
            
            // 最终渲染消息
            finalRenderMessage(messageElement, content, reasoningContent) {
                const contentElement = messageElement.querySelector('.message-content');
                
                if (this.autoRender) {
                    contentElement.classList.remove('raw-text');
                    this.renderAndSetContent(contentElement, content);
                } else {
                    contentElement.textContent = content;
                    contentElement.classList.add('raw-text');
                }
                
                this.removeStreamingIndicator(contentElement);
                
                if (!reasoningContent) {
                    const reasoningElement = messageElement.querySelector('.reasoning-content');
                    if (reasoningElement) reasoningElement.remove();
                }
            }
            
            // 渲染并设置内容(完整版)
            renderAndSetContent(contentElement, content) {
                const originalText = content;
                const html = this.renderMarkdown(content);
                contentElement.innerHTML = `<div class="markdown-content">${html}</div>`;
                contentElement.dataset.originalText = originalText;
                
                if (this.enableLatex && window.renderMathInElement) {
                    setTimeout(() => {
                        try {
                            renderMathInElement(contentElement, {
                                delimiters: [
                                    {left: "$$", right: "$$", display: true},
                                    {left: "$", right: "$", display: false},
                                    {left: "\\(", right: "\\)", display: false},
                                    {left: "\\[", right: "\\]", display: true},
                                    {left: "\\begin{equation}", right: "\\end{equation}", display: true},
                                    {left: "\\begin{equation*}", right: "\\end{equation*}", display: true},
                                    {left: "\\begin{matrix}", right: "\\end{matrix}", display: true},
                                    {left: "\\begin{bmatrix}", right: "\\end{bmatrix}", display: true},
                                    {left: "\\begin{pmatrix}", right: "\\end{pmatrix}", display: true},
                                    {left: "\\begin{vmatrix}", right: "\\end{vmatrix}", display: true},
                                    {left: "\\begin{Vmatrix}", right: "\\end{Vmatrix}", display: true},
                                    {left: "\\begin{align}", right: "\\end{align}", display: true},
                                    {left: "\\begin{align*}", right: "\\end{align*}", display: true},
                                    {left: "\\begin{aligned}", right: "\\end{aligned}", display: true},
                                    {left: "\\begin{gather}", right: "\\end{gather}", display: true},
                                    {left: "\\begin{gather*}", right: "\\end{gather*}", display: true},
                                    {left: "\\begin{cases}", right: "\\end{cases}", display: true},
                                    {left: "\\begin{array}", right: "\\end{array}", display: true},
                                    {left: "\\begin{smallmatrix}", right: "\\end{smallmatrix}", display: true}
                                ],
                                throwOnError: false,
                                errorColor: "#cc0000"
                            });
                        } catch (e) {
                            console.error('LaTeX渲染错误:', e);
                        }
                    }, 10);
                }
            }
            
            // 渲染Markdown(保持原有)
            renderMarkdown(text) {
                let html = this.escapeHtml(text);
                
                // 代码块
                html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
                    const language = lang || 'text';
                    return `<div class="code-block"><span class="code-language">${language}</span><pre><code>${this.escapeHtml(code)}</code></pre></div>`;
                });
                
                html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
                
                html = html.replace(/^#{6} (.*$)/gim, '<h6>$1</h6>');
                html = html.replace(/^#{5} (.*$)/gim, '<h5>$1</h5>');
                html = html.replace(/^#{4} (.*$)/gim, '<h4>$1</h4>');
                html = html.replace(/^#{3} (.*$)/gim, '<h3>$1</h3>');
                html = html.replace(/^#{2} (.*$)/gim, '<h2>$1</h2>');
                html = html.replace(/^#{1} (.*$)/gim, '<h1>$1</h1>');
                
                html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
                html = html.replace(/__(.*?)__/g, '<strong>$1</strong>');
                html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
                html = html.replace(/_(.*?)_/g, '<em>$1</em>');
                html = html.replace(/~~(.*?)~~/g, '<del>$1</del>');
                
                html = html.replace(/^> (.*$)/gim, '<blockquote>$1</blockquote>');
                
                html = html.replace(/^\s*[-*+] (.+)$/gim, '<li>$1</li>');
                html = html.replace(/(<li>.*?<\/li>\s*)+/gs, '<ul>$&</ul>');
                
                html = html.replace(/^\s*\d+\. (.+)$/gim, '<li>$1</li>');
                html = html.replace(/(<li>.*?<\/li>\s*)+/gs, '<ol>$&</ol>');
                
                html = html.replace(/^-{3,}$/gim, '<hr>');
                
                html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
                html = html.replace(/!\[([^\]]+)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">');
                
                const tableRegex = /\|(.+)\|[\r\n]+\|([-:| -]+)\|[\r\n]+((?:\|.+\|[\r\n]*)+)/g;
                html = html.replace(tableRegex, (match, header, align, rows) => {
                    const headers = header.split('|').filter(h => h.trim()).map(h => `<th>${h.trim()}</th>`).join('');
                    const rowsArray = rows.trim().split('\n');
                    const rowsHtml = rowsArray.map(row => {
                        const cells = row.split('|').filter(c => c.trim()).map(c => `<td>${c.trim()}</td>`).join('');
                        return `<tr>${cells}</tr>`;
                    }).join('');
                    return `<table><thead><tr>${headers}</tr></thead><tbody>${rowsHtml}</tbody></table>`;
                });
                
                html = html.replace(/\n\n+/g, '</p><p>');
                html = html.replace(/\n/g, '<br>');
                html = '<p>' + html + '</p>';
                html = html.replace(/<p><\/p>/g, '');
                
                return html;
            }
            
            // 添加系统消息
            addSystemMessage(content) {
                const msgId = this.generateId();
                this.currentConversation.messages.unshift({
                    role: 'system',
                    content: content,
                    timestamp: Date.now(),
                    messageId: msgId
                });
                this.addMessageToUI(content, 'system', msgId);
            }
            
            // 创建思考指示器(保持原有)
            createThinkingIndicator() {
                const element = document.createElement('div');
                element.className = 'message assistant-message';
                element.innerHTML = `
                    <div class="message-header">
                        <div class="message-avatar assistant-avatar">AI</div>
                        <div class="message-sender assistant-sender">AI助手</div>
                        <div class="message-time">${this.formatTime(new Date())}</div>
                    </div>
                    <div class="typing-indicator">
                        思考中
                        <div class="typing-dots">
                            <span></span>
                            <span></span>
                            <span></span>
                        </div>
                    </div>
                `;
                return element;
            }
            
            // 更新使用量和费用
            updateUsage(usage) {
                this.currentConversation.usage = {
                    prompt_cache_hit_tokens: usage.prompt_cache_hit_tokens || 0,
                    prompt_cache_miss_tokens: usage.prompt_cache_miss_tokens || 0,
                    completion_tokens: usage.completion_tokens || 0,
                    total_tokens: usage.total_tokens || 0
                };
                
                const cost = this.calculateCost(usage);
                this.totalCost += cost;
                this.totalTokens += usage.total_tokens || 0;
                
                document.getElementById('totalCost').textContent = this.totalCost.toFixed(6) + ' 元';
                document.getElementById('totalTokens').textContent = this.totalTokens;
                
                this.saveSettings();
            }
            
            // 计算费用
            calculateCost(usage) {
                const hitTokens = usage.prompt_cache_hit_tokens || 0;
                const missTokens = usage.prompt_cache_miss_tokens || 0;
                const outputTokens = usage.completion_tokens || 0;
                
                const hitCost = (hitTokens / 1000000) * this.prices.cacheHit;
                const missCost = (missTokens / 1000000) * this.prices.cacheMiss;
                const outputCost = (outputTokens / 1000000) * this.prices.output;
                
                return hitCost + missCost + outputCost;
            }
            
            // 切换深度思考模式
            toggleReasoningModel() {
                this.useReasoningModel = !this.useReasoningModel;
                this.params.model = this.useReasoningModel ? 'deepseek-reasoner' : 'deepseek-chat';
                
                document.getElementById('modelSelect').value = this.params.model;
                document.getElementById('currentModel').textContent = this.params.model;
                document.getElementById('reasoningStatus').textContent = this.useReasoningModel ? '开启' : '关闭';
                
                this.showAlert(`深度思考模式已${this.useReasoningModel ? '开启' : '关闭'}`, 'info');
            }
            
            // 切换自动Markdown渲染
            toggleAutoRender() {
                this.autoRender = !this.autoRender;
                document.getElementById('renderStatus').textContent = this.autoRender ? '开启' : '关闭';
                this.showAlert(`Markdown自动渲染已${this.autoRender ? '开启' : '关闭'}`, 'info');
                this.rerenderAllMessages();
                this.saveSettings();
            }
            
            // 切换LaTeX渲染
            toggleLatex() {
                this.enableLatex = !this.enableLatex;
                document.getElementById('latexStatus').textContent = this.enableLatex ? '开启' : '关闭';
                this.showAlert(`LaTeX公式渲染已${this.enableLatex ? '开启' : '关闭'}`, 'info');
                this.rerenderAllMessages();
                this.saveSettings();
            }
            
            // 重新渲染所有消息
            rerenderAllMessages() {
                const messages = document.querySelectorAll('.message');
                messages.forEach(messageElement => {
                    const contentElement = messageElement.querySelector('.message-content');
                    if (contentElement) {
                        const role = messageElement.classList.contains('user-message') ? 'user' : 
                                    messageElement.classList.contains('assistant-message') ? 'assistant' : 'system';
                        
                        if (role === 'assistant') {
                            const originalText = contentElement.dataset.originalText || contentElement.textContent;
                            if (this.autoRender) {
                                this.renderAndSetContent(contentElement, originalText);
                            } else {
                                contentElement.textContent = originalText;
                                contentElement.classList.add('raw-text');
                            }
                        }
                    }
                });
            }
            
            // 新对话
            newConversation() {
                if (this.currentConversation.messages.length > 0) {
                    this.saveCurrentConversationToLocal();
                }
                
                this.currentConversation = {
                    id: this.generateId(),
                    title: '新对话',
                    messages: [],
                    timestamp: Date.now(),
                    usage: {
                        prompt_cache_hit_tokens: 0,
                        prompt_cache_miss_tokens: 0,
                        completion_tokens: 0,
                        total_tokens: 0
                    }
                };
                
                this.hasSystemMessageInConversation = false;
                
                const container = document.getElementById('messagesContainer');
                container.innerHTML = '';
                
                const emptyChat = document.createElement('div');
                emptyChat.className = 'empty-chat';
                emptyChat.id = 'emptyChat';
                emptyChat.innerHTML = `
                    <i class="fas fa-robot"></i>
                    <h3>新对话已开始</h3>
                    <p>输入消息开始与DeepSeek AI对话</p>
                `;
                container.appendChild(emptyChat);
                
                this.showAlert('已开始新对话', 'success');
            }
            
            // 保存当前对话到本地存储
            saveCurrentConversationToLocal() {
                if (this.currentConversation.messages.length === 0) return;
                
                const firstUserMessage = this.currentConversation.messages.find(msg => msg.role === 'user');
                if (firstUserMessage) {
                    const title = firstUserMessage.content.substring(0, 30);
                    this.currentConversation.title = title.length < firstUserMessage.content.length ? title + '...' : title;
                }
                
                this.conversations.push({...this.currentConversation});
                this.saveConversations();
            }
            
            // 保存对话到文件
            saveConversationToFile() {
                if (this.currentConversation.messages.length === 0) {
                    this.showAlert('当前对话为空,无需保存', 'warning');
                    return;
                }
                
                let text = '';
                this.currentConversation.messages.forEach(msg => {
                    let rolePrefix = '';
                    switch (msg.role) {
                        case 'user': rolePrefix = 'u>'; break;
                        case 'assistant': rolePrefix = 'a>'; break;
                        case 'system': rolePrefix = 's>'; break;
                    }
                    text += `${rolePrefix}${msg.content}\n\n`;
                });
                
                const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
                a.download = `deepseek_chat_${timestamp}.txt`;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
                
                this.showAlert('对话已保存为.txt文件', 'success');
            }
            
            // 从文件加载对话
            loadConversationFromFile() {
                const input = document.createElement('input');
                input.type = 'file';
                input.accept = '.txt';
                input.style.display = 'none';
                document.body.appendChild(input);
                
                input.addEventListener('change', (event) => {
                    const file = event.target.files[0];
                    if (!file) return;
                    
                    const reader = new FileReader();
                    reader.onload = (e) => {
                        this.parseAndLoadConversation(e.target.result);
                    };
                    reader.readAsText(file);
                    document.body.removeChild(input);
                });
                
                input.click();
            }
            
            // 解析并加载对话
            parseAndLoadConversation(content) {
                this.newConversation();
                
                const container = document.getElementById('messagesContainer');
                container.innerHTML = '';
                
                const lines = content.split('\n');
                let currentRole = '';
                let currentContent = '';
                const messages = [];
                const rolePattern = /^(u>|a>|s>)(.*)/;
                
                for (let i = 0; i < lines.length; i++) {
                    const line = lines[i];
                    const match = line.match(rolePattern);
                    
                    if (match) {
                        if (currentRole && currentContent) {
                            messages.push({
                                role: currentRole,
                                content: currentContent.trim()
                            });
                        }
                        
                        const prefix = match[1];
                        switch (prefix) {
                            case 'u>': currentRole = 'user'; break;
                            case 'a>': currentRole = 'assistant'; break;
                            case 's>': currentRole = 'system'; break;
                        }
                        currentContent = match[2];
                    } else if (currentRole) {
                        currentContent += '\n' + line;
                    }
                }
                
                if (currentRole && currentContent) {
                    messages.push({
                        role: currentRole,
                        content: currentContent.trim()
                    });
                }
                
                const hasSystemMessage = messages.some(msg => msg.role === 'system');
                if (hasSystemMessage) this.hasSystemMessageInConversation = true;
                
                messages.forEach(msg => {
                    const msgId = this.generateId();
                    msg.messageId = msgId;
                    this.currentConversation.messages.push(msg);
                    this.addMessageToUI(msg.content, msg.role, msgId);
                });
                
                this.showAlert('对话已从文件加载', 'success');
            }
            
            // 更新UI
            updateUI() {
                document.getElementById('totalCost').textContent = this.totalCost.toFixed(6) + ' 元';
                document.getElementById('totalTokens').textContent = this.totalTokens;
                document.getElementById('currentModel').textContent = this.params.model;
                
                document.getElementById('renderStatus').textContent = this.autoRender ? '开启' : '关闭';
                document.getElementById('reasoningStatus').textContent = this.useReasoningModel ? '开启' : '关闭';
                document.getElementById('latexStatus').textContent = this.enableLatex ? '开启' : '关闭';
            }
            
            // 显示提示信息
            showAlert(message, type) {
                const existingAlert = document.querySelector('.alert');
                if (existingAlert) existingAlert.remove();
                
                const alert = document.createElement('div');
                alert.className = `alert alert-${type}`;
                alert.innerHTML = `
                    <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'warning' ? 'exclamation-triangle' : type === 'danger' ? 'times-circle' : 'info-circle'}"></i>
                    ${message}
                `;
                
                const settingsContent = document.querySelector('.settings-content');
                if (settingsContent) {
                    settingsContent.insertBefore(alert, settingsContent.firstChild);
                    setTimeout(() => {
                        if (alert.parentNode) alert.remove();
                    }, 3000);
                }
            }
            
            // 滚动到底部
            scrollToBottom() {
                const container = document.getElementById('messagesContainer');
                container.scrollTop = container.scrollHeight;
            }
            
            // 格式化时间
            formatTime(date) {
                return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
            }
            
            // HTML转义
            escapeHtml(text) {
                const div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            }
        }
        
        // 初始化应用
        const deepSeekClient = new DeepSeekClient();
        window.deepSeekClient = deepSeekClient;
    </script>
</body>
</html>
posted @ 2026-03-12 15:07  樓影沫瞬_Hz17  阅读(3)  评论(0)    收藏  举报