<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}智联青云{% endblock %}</title>
    <!-- Google Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: { 
                        primary: '#3b82f6',
                        secondary: '#10b981',
                        accent: '#8b5cf6',
                        surface: '#ffffff',
                        background: '#f8fafc',
                    },
                    fontFamily: {
                        sans: ['Inter', 'sans-serif'],
                    },
                    boxShadow: {
                        'soft': '0 4px 20px -2px rgba(0, 0, 0, 0.05)',
                        'glass': '0 8px 32px 0 rgba(31, 38, 135, 0.07)',
                    }
                }
            }
        }
    </script>
    <link rel="stylesheet" href="/static/css/style.css">
    <link rel="stylesheet" href="/static/css/rich-editor.css">
    <!-- KaTeX 数学公式渲染 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css">
    <script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"
        onload="document.addEventListener('DOMContentLoaded',function(){renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],throwOnError:false});})"></script>
    <script src="/static/js/rich-editor.js"></script>
</head>
<body class="min-h-screen bg-slate-50 font-sans text-slate-800 antialiased selection:bg-primary/30 selection:text-slate-900">
    {% block navbar %}
    <nav class="sticky top-0 z-40 bg-white/80 backdrop-blur-md border-b border-slate-200/80 shadow-sm transition-all duration-300">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex justify-between h-16">
                <div class="flex">
                    <a href="/" class="flex-shrink-0 flex items-center">
                        <span class="text-xl font-bold text-primary">智联青云</span>
                    </a>
                    <div class="hidden sm:ml-6 sm:flex sm:space-x-8">
                        <a href="/contests" class="border-transparent text-slate-500 hover:border-primary hover:text-slate-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
                            <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3l14 9-14 9V3z"/></svg>
                            杯赛专栏
                        </a>
                        <a href="/exams" class="border-transparent text-slate-500 hover:border-primary hover:text-slate-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
                            <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/></svg>
                            考试系统
                        </a>
                        <a href="/forum" class="border-transparent text-slate-500 hover:border-primary hover:text-slate-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
                            <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
                            社区论坛
                        </a>
                        <a href="/chat" class="border-transparent text-slate-500 hover:border-primary hover:text-slate-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
                            <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
                            消息
                        </a>
                        <a href="/profile" class="border-transparent text-slate-500 hover:border-primary hover:text-slate-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
                            <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
                            个人
                        </a>
                        {% if user and user.role == 'student' %}
                        <a href="/apply-teacher" class="border-transparent text-slate-500 hover:border-primary hover:text-slate-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
                            <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
                            申请老师
                        </a>
                        {% endif %}
                    </div>
                </div>
                <!-- 移动端汉堡菜单按钮 -->
                <div class="flex items-center sm:hidden">
                    <button onclick="document.getElementById('mobileMenu').classList.toggle('hidden')" class="text-slate-500 hover:text-slate-700 p-2" aria-label="菜单">
                        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
                    </button>
                </div>
                <div class="hidden sm:ml-6 sm:flex sm:items-center">
                    {% if user %}
                    <div class="flex items-center space-x-4">
                        <!-- 通知铃铛 -->
                        <div class="relative" id="notif-wrapper">
                            <button onclick="toggleNotifPanel()" class="text-slate-500 hover:text-slate-700 relative" title="通知">
                                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
                                <span id="notifBadge" class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center hidden" style="font-size:10px">0</span>
                            </button>
                            <div id="notifPanel" class="hidden absolute right-0 top-10 w-96 max-h-[500px] bg-white rounded-lg shadow-xl border border-slate-200 z-50 overflow-hidden">
                                <div class="px-4 py-3 border-b border-slate-200 flex items-center justify-between">
                                    <span class="font-medium text-sm text-slate-900">通知</span>
                                    <a href="/chat?tab=notif" class="text-xs text-primary hover:underline">查看全部</a>
                                </div>
                                <div id="notifList" class="overflow-y-auto max-h-[440px] divide-y divide-slate-100"></div>
                            </div>
                        </div>
                        {% if user.role == 'admin' or user.role == 'teacher' %}
                        <a href="/admin" class="text-slate-500 hover:text-slate-700" title="管理后台">
                            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
                            </svg>
                        </a>
                        {% endif %}
                        <span class="text-sm text-slate-700">
                            <svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
                            {{ get_user_display_name() }} ({{ '管理员' if user.role == 'admin' else ('老师' if user.role == 'teacher' else '学生') }})
                        </span>
                        <a href="/logout" class="text-slate-500 hover:text-slate-700">
                            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>
                        </a>
                    </div>
                    {% else %}
                    <div class="space-x-4">
                        <a href="/login" class="text-slate-500 hover:text-slate-700 text-sm font-medium">登录</a>
                        <a href="/register" class="bg-primary text-white px-4 py-2 rounded-lg shadow-sm text-sm font-medium hover:bg-blue-600 hover:shadow transition-all duration-200 transform hover:-translate-y-0.5">注册</a>
                    </div>
                    {% endif %}
                </div>
            </div>
        </div>
        <!-- 移动端下拉菜单 -->
        <div id="mobileMenu" class="hidden sm:hidden border-t border-slate-200 bg-white/95">
            <div class="px-4 py-3 space-y-2">
                <a href="/contests" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3l14 9-14 9V3z"/></svg>
                    杯赛专栏
                </a>
                <a href="/exams" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/></svg>
                    考试系统
                </a>
                <a href="/forum" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
                    社区论坛
                </a>
                <a href="/chat" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
                    消息
                </a>
                <a href="/profile" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
                    个人
                </a>
                {% if user and user.role == 'student' %}
                <a href="/apply-teacher" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
                    申请老师
                </a>
                {% endif %}
                {% if user and user.role == 'admin' %}
                <a href="/admin" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">
                    <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
                    管理后台
                </a>
                {% endif %}
                <div class="border-t border-slate-200 pt-2 mt-2">
                    {% if user %}
                    <span class="block px-3 py-2 text-sm text-slate-500">{{ get_user_display_name() }} ({{ '管理员' if user.role == 'admin' else ('老师' if user.role == 'teacher' else '学生') }})</span>
                    <a href="/logout" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-red-600 hover:bg-red-50">
                        <svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>
                        退出登录
                    </a>
                    {% else %}
                    <a href="/login" class="block px-3 py-2 rounded-md text-sm font-medium text-slate-700 hover:bg-slate-100">登录</a>
                    <a href="/register" class="block px-3 py-2 rounded-md text-sm font-medium text-primary hover:bg-blue-50">注册</a>
                    {% endif %}
                </div>
            </div>
        </div>
    </nav>
    {% endblock %}

    <main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
        {% block content %}{% endblock %}
    </main>

    {% block scripts %}{% endblock %}

    {% if user %}
    <!-- 通知面板脚本 -->
    <script>
    (function(){
        function toggleNotifPanel() {
            const panel = document.getElementById('notifPanel');
            panel.classList.toggle('hidden');
            if (!panel.classList.contains('hidden')) loadNotifications();
        }
        window.toggleNotifPanel = toggleNotifPanel;

        // 点击外部关闭
        document.addEventListener('click', function(e) {
            const wrapper = document.getElementById('notif-wrapper');
            if (wrapper && !wrapper.contains(e.target)) {
                document.getElementById('notifPanel').classList.add('hidden');
            }
        });

        function updateNotifBadge() {
            fetch('/api/notifications/unread-count').then(r=>r.json()).then(d=>{
                const badge = document.getElementById('notifBadge');
                if (d.count > 0) {
                    badge.textContent = d.count > 99 ? '99+' : d.count;
                    badge.classList.remove('hidden');
                } else {
                    badge.classList.add('hidden');
                }
            }).catch(()=>{});
        }
        updateNotifBadge();
        setInterval(updateNotifBadge, 30000);

        function loadNotifications() {
            fetch('/api/notifications').then(r=>r.json()).then(d=>{
                if (!d.success) return;
                const list = document.getElementById('notifList');
                if (d.notifications.length === 0) {
                    list.innerHTML = '<div class="px-4 py-8 text-center text-slate-400 text-sm">暂无通知</div>';
                    return;
                }
                list.innerHTML = d.notifications.map(n => {
                    let actions = '';
                    const typeIcons = {'teacher_application':'👨‍🏫','teacher_result':'🎓','contest_application':'🏆','contest_result':'🏅','contest_new_exam':'📝','exam_graded':'✅'};
                    const icon = typeIcons[n.type] || '🔔';
                    // 教师申请：显示同意/拒绝按钮
                    if (n.type === 'teacher_application' && n.application_status === 'pending' && n.application_id) {
                        actions = `<div class="flex gap-2 mt-2">
                            <button onclick="event.stopPropagation();approveTeacher(${n.application_id},${n.id})" class="px-3 py-1 text-xs bg-green-500 text-white rounded hover:bg-green-600">同意</button>
                            <button onclick="event.stopPropagation();rejectTeacher(${n.application_id},${n.id})" class="px-3 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600">拒绝</button>
                        </div>`;
                    } else if (n.type === 'teacher_application' && n.application_status === 'approved') {
                        actions = '<div class="text-xs text-green-600 mt-1">✅ 已同意</div>';
                    } else if (n.type === 'teacher_application' && n.application_status === 'rejected') {
                        actions = '<div class="text-xs text-red-600 mt-1">❌ 已拒绝</div>';
                    }
                    // 杯赛申请：显示同意/拒绝按钮（仅管理员）
                    if (n.type === 'contest_application' && n.application_status === 'pending' && n.post_id) {
                        actions = `<div class="flex gap-2 mt-2">
                            <button onclick="event.stopPropagation();approveContest(${n.post_id},${n.id})" class="px-3 py-1 text-xs bg-green-500 text-white rounded hover:bg-green-600">同意</button>
                            <button onclick="event.stopPropagation();rejectContest(${n.post_id},${n.id})" class="px-3 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600">拒绝</button>
                        </div>`;
                    } else if (n.type === 'contest_application' && n.application_status === 'approved') {
                        actions = '<div class="text-xs text-green-600 mt-1">✅ 已同意</div>';
                    } else if (n.type === 'contest_application' && n.application_status === 'rejected') {
                        actions = '<div class="text-xs text-red-600 mt-1">❌ 已拒绝</div>';
                    }
                    const unreadDot = n.read ? '' : '<span class="w-2 h-2 bg-blue-500 rounded-full flex-shrink-0"></span>';
                    return `<div class="px-4 py-3 hover:bg-slate-50 cursor-pointer flex gap-2 items-start ${n.read?'':'bg-blue-50/50'}" onclick="markRead(${n.id},this)">
                        <span class="text-lg flex-shrink-0">${icon}</span>
                        <div class="flex-1 min-w-0">
                            <div class="text-sm text-slate-700">${escNotif(n.content)}</div>
                            <div class="text-xs text-slate-400 mt-1">${n.created_at}</div>
                            ${actions}
                        </div>
                        ${unreadDot}
                    </div>`;
                }).join('');
            });
        }

        function markRead(nid, el) {
            fetch(`/api/notifications/${nid}/read`, {method:'POST'});
            el.classList.remove('bg-blue-50/50');
            const dot = el.querySelector('.bg-blue-500');
            if (dot) dot.remove();
            updateNotifBadge();
        }
        window.markRead = markRead;

        window.approveTeacher = async function(appId, nid) {
            try {
                const res = await fetch(`/api/teacher-applications/${appId}/approve`, {method:'POST'});
                const d = await res.json();
                alert(d.message);
                loadNotifications();
                updateNotifBadge();
            } catch(e) { alert('操作失败'); }
        };
        window.rejectTeacher = async function(appId, nid) {
            if (!confirm('确定拒绝该申请？')) return;
            try {
                const res = await fetch(`/api/teacher-applications/${appId}/reject`, {method:'POST'});
                const d = await res.json();
                alert(d.message);
                loadNotifications();
                updateNotifBadge();
            } catch(e) { alert('操作失败'); }
        };
        window.approveContest = async function(appId, nid) {
            try {
                const res = await fetch(`/api/contest-applications/${appId}/approve`, {method:'POST'});
                const d = await res.json();
                alert(d.message);
                loadNotifications();
                updateNotifBadge();
            } catch(e) { alert('操作失败'); }
        };
        window.rejectContest = async function(appId, nid) {
            if (!confirm('确定拒绝该申请？')) return;
            try {
                const res = await fetch(`/api/contest-applications/${appId}/reject`, {method:'POST'});
                const d = await res.json();
                alert(d.message);
                loadNotifications();
                updateNotifBadge();
            } catch(e) { alert('操作失败'); }
        };

        function escNotif(s) { if(!s)return''; const d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
    })();
    </script>

    <!-- 浮动聊天气泡 -->
    <div id="chatBubble" class="fixed bottom-6 right-6 z-40">
        <button onclick="toggleMiniChat()" class="w-14 h-14 bg-gradient-to-tr from-primary to-blue-400 text-white rounded-full shadow-lg hover:shadow-xl hover:bg-blue-600 flex items-center justify-center relative transition-all duration-300 hover:scale-110">
            <svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
            <span id="bubbleBadge" class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center hidden">0</span>
        </button>
    </div>

    <!-- 迷你聊天窗口 -->
    <div id="miniChat" class="fixed bottom-24 right-6 w-[380px] h-[500px] bg-white rounded-xl shadow-2xl border border-slate-200 z-40 hidden flex flex-col overflow-hidden">
        <div class="px-4 py-3 bg-primary text-white flex justify-between items-center rounded-t-xl">
            <span class="font-medium text-sm" id="miniTitle">消息</span>
            <div class="flex items-center gap-3">
                <a href="/chat" class="text-white/80 hover:text-white text-xs">打开完整版</a>
                <button onclick="toggleMiniChat()" class="text-white/80 hover:text-white">&times;</button>
            </div>
        </div>
        <div id="miniRoomList" class="flex-1 overflow-y-auto"></div>
        <div id="miniChatView" class="flex-1 flex-col hidden">
            <div class="px-3 py-2 border-b border-slate-200 flex items-center gap-2">
                <button onclick="miniBack()" class="text-slate-400 hover:text-slate-600"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg></button>
                <span id="miniChatName" class="text-sm font-medium truncate"></span>
            </div>
            <div id="miniMessages" class="flex-1 overflow-y-auto px-3 py-2 space-y-2" style="max-height:340px"></div>
            <div class="px-3 py-2 border-t border-slate-200 flex gap-2">
                <input id="miniInput" type="text" placeholder="输入消息..." class="flex-1 px-3 py-1.5 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary/50" onkeydown="if(event.key==='Enter'){event.preventDefault();miniSend();}">
                <button onclick="miniSend()" class="px-3 py-1.5 bg-primary text-white rounded-lg text-sm hover:bg-blue-600">发送</button>
            </div>
        </div>
    </div>

    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
    <script>
    (function(){
        if (document.getElementById('chatApp')) return; // chat.html 已有自己的socket
        const bubbleSocket = io();
        let miniRoomId = null;
        let miniRooms = [];

        function updateBadge() {
            fetch('/api/chat/unread-total').then(r=>r.json()).then(d=>{
                const badge = document.getElementById('bubbleBadge');
                if (d.total > 0) {
                    badge.textContent = d.total > 99 ? '99+' : d.total;
                    badge.classList.remove('hidden');
                } else {
                    badge.classList.add('hidden');
                }
            }).catch(()=>{});
        }
        updateBadge();
        setInterval(updateBadge, 30000);

        bubbleSocket.on('new_message', (msg) => {
            updateBadge();
            // 气泡抖动
            const btn = document.querySelector('#chatBubble button');
            btn.classList.add('animate-bounce');
            setTimeout(() => btn.classList.remove('animate-bounce'), 1000);
            // 迷你聊天窗口更新
            if (miniRoomId === msg.room_id) {
                appendMiniMsg(msg);
            }
            loadMiniRooms();
        });

        bubbleSocket.on('message_recalled', () => {
            if (miniRoomId) loadMiniMessages(miniRoomId);
        });

        window.toggleMiniChat = function() {
            const mc = document.getElementById('miniChat');
            mc.classList.toggle('hidden');
            if (!mc.classList.contains('hidden')) {
                loadMiniRooms();
            }
        };

        function loadMiniRooms() {
            fetch('/api/chat/rooms').then(r=>r.json()).then(d=>{
                if (!d.success) return;
                miniRooms = d.rooms;
                const list = document.getElementById('miniRoomList');
                list.innerHTML = d.rooms.map(r => `
                    <div class="flex items-center gap-2 px-3 py-2.5 cursor-pointer hover:bg-slate-50 border-b border-slate-50" onclick="miniOpenRoom(${r.id})">
                        <div class="w-9 h-9 rounded-full bg-slate-200 flex items-center justify-center flex-shrink-0 overflow-hidden">
                            ${r.avatar ? `<img src="${r.avatar}" class="w-full h-full object-cover">` : `<span class="text-xs text-slate-500">${(r.name||'?')[0]}</span>`}
                        </div>
                        <div class="flex-1 min-w-0">
                            <div class="flex justify-between"><span class="text-sm font-medium truncate">${esc(r.name||'')}</span>
                            ${r.unread>0?`<span class="bg-red-500 text-white text-xs rounded-full px-1.5 min-w-[16px] text-center">${r.unread}</span>`:''}</div>
                            <p class="text-xs text-slate-400 truncate">${r.last_message?esc(r.last_message.content||''):'暂无消息'}</p>
                        </div>
                    </div>
                `).join('');
            });
        }

        window.miniOpenRoom = function(roomId) {
            miniRoomId = roomId;
            const room = miniRooms.find(r=>r.id===roomId);
            document.getElementById('miniTitle').textContent = room?.name || '聊天';
            document.getElementById('miniChatName').textContent = room?.name || '聊天';
            document.getElementById('miniRoomList').classList.add('hidden');
            const cv = document.getElementById('miniChatView');
            cv.classList.remove('hidden');
            cv.classList.add('flex');
            loadMiniMessages(roomId);
            fetch(`/api/chat/rooms/${roomId}/read`, {method:'POST'});
            updateBadge();
        };

        window.miniBack = function() {
            miniRoomId = null;
            document.getElementById('miniRoomList').classList.remove('hidden');
            const cv = document.getElementById('miniChatView');
            cv.classList.add('hidden');
            cv.classList.remove('flex');
            document.getElementById('miniTitle').textContent = '消息';
            loadMiniRooms();
        };

        function loadMiniMessages(roomId) {
            fetch(`/api/chat/rooms/${roomId}/messages?limit=20`).then(r=>r.json()).then(d=>{
                if (!d.success) return;
                const area = document.getElementById('miniMessages');
                const currUserStr = '{{ user | tojson | safe }}';
                const currUser = currUserStr ? JSON.parse(currUserStr) : null;
                area.innerHTML = d.messages.map(m => {
                    if (m.type==='system') return `<div class="text-center"><span class="text-xs text-slate-400">${esc(m.content)}</span></div>`;
                    if (m.recalled) return `<div class="text-center"><span class="text-xs text-slate-400">${esc(m.sender_name)} 撤回了一条消息</span></div>`;
                    const isMe = m.sender_id === currUser.id;
                    const bubble = isMe ? 'bg-primary text-white ml-auto' : 'bg-slate-100 text-slate-800';
                    let content = m.type==='image'?'[图片]':m.type==='file'?'[文件]':esc(m.content);
                    return `<div class="${isMe?'text-right':'text-left'}">
                        ${!isMe?`<div class="text-xs text-slate-400 mb-0.5">${esc(m.sender_name)}</div>`:''}
                        <div class="inline-block px-3 py-1.5 rounded-lg text-sm max-w-[80%] ${bubble}">${content}</div>
                    </div>`;
                }).join('');
                area.scrollTop = area.scrollHeight;
            });
        }

        function appendMiniMsg(msg) {
            const area = document.getElementById('miniMessages');
            const currUserStr = '{{ user | tojson | safe }}';
            const currUser = currUserStr ? JSON.parse(currUserStr) : null;
            const isMe = currUser && msg.sender_id === currUser.id;
            if (msg.type==='system') {
                area.innerHTML += `<div class="text-center"><span class="text-xs text-slate-400">${esc(msg.content)}</span></div>`;
            } else {
                const bubble = isMe ? 'bg-primary text-white ml-auto' : 'bg-slate-100 text-slate-800';
                let content = msg.type==='image'?'[图片]':msg.type==='file'?'[文件]':(typeof renderRichContent==='function'?renderRichContent(msg.content):esc(msg.content));
                area.innerHTML += `<div class="${isMe?'text-right':'text-left'}">
                    ${!isMe?`<div class="text-xs text-slate-400 mb-0.5">${esc(msg.sender_name)}</div>`:''}
                    <div class="inline-block px-3 py-1.5 rounded-lg text-sm max-w-[80%] ${bubble}">${content}</div>
                </div>`;
            }
            area.scrollTop = area.scrollHeight;
        }

        window.miniSend = function() {
            const input = document.getElementById('miniInput');
            const content = input.value.trim();
            if (!content || !miniRoomId) return;
            bubbleSocket.emit('send_message', { room_id: miniRoomId, type: 'text', content: content });
            input.value = '';
        };

        function esc(s) { if(!s)return''; const d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
    })();
    </script>
    {% endif %}
</body>
</html>