<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }} - DataLabel</title>
    <style>
        :root {
            --primary: #4f46e5;
            --primary-light: #818cf8;
            --success: #10b981;
            --warning: #f59e0b;
            --danger: #ef4444;
            --gray-50: #f9fafb;
            --gray-100: #f3f4f6;
            --gray-200: #e5e7eb;
            --gray-300: #d1d5db;
            --gray-500: #6b7280;
            --gray-700: #374151;
            --gray-900: #111827;
            --bg: var(--gray-50);
            --card-bg: white;
            --text: var(--gray-900);
            --text-secondary: var(--gray-500);
            --border: var(--gray-200);
            --input-bg: white;
        }

        [data-theme="dark"] {
            --primary: #6366f1;
            --primary-light: #818cf8;
            --success: #34d399;
            --warning: #fbbf24;
            --danger: #f87171;
            --gray-50: #1a1a2e;
            --gray-100: #1f2037;
            --gray-200: #2d2d44;
            --gray-300: #3d3d56;
            --gray-500: #9ca3af;
            --gray-700: #d1d5db;
            --gray-900: #f3f4f6;
            --bg: #0f0f1a;
            --card-bg: #1a1a2e;
            --text: #f3f4f6;
            --text-secondary: #9ca3af;
            --border: #2d2d44;
            --input-bg: #1f2037;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background: var(--bg);
            color: var(--text);
            line-height: 1.6;
        }

        /* Header */
        header {
            background: var(--card-bg);
            border-bottom: 1px solid var(--border);
            padding: 1rem 2rem;
            position: sticky;
            top: 0;
            z-index: 100;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .header-right {
            display: flex;
            align-items: center;
            gap: 1rem;
        }

        .theme-toggle {
            background: none;
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 0.4rem 0.6rem;
            cursor: pointer;
            font-size: 1rem;
            color: var(--text);
            transition: background 0.2s;
        }

        .theme-toggle:hover {
            background: var(--gray-100);
        }

        header h1 {
            font-size: 1.25rem;
            font-weight: 600;
        }

        .progress-info {
            display: flex;
            align-items: center;
            gap: 1rem;
        }

        .progress-bar {
            width: 200px;
            height: 8px;
            background: var(--gray-200);
            border-radius: 4px;
            overflow: hidden;
        }

        .progress-bar-fill {
            height: 100%;
            background: var(--primary);
            transition: width 0.3s ease;
        }

        .progress-text {
            font-size: 0.875rem;
            color: var(--text-secondary);
        }

        /* Main Layout */
        main {
            max-width: 1400px;
            margin: 0 auto;
            padding: 2rem;
            display: grid;
            grid-template-columns: 220px 1fr 350px;
            gap: 1.5rem;
        }

        @media (max-width: 1100px) {
            main {
                grid-template-columns: 1fr 320px;
            }
            .task-sidebar { display: none; }
        }

        @media (max-width: 900px) {
            main {
                grid-template-columns: 1fr;
            }
            .task-sidebar { display: none; }
        }

        /* Task Sidebar */
        .task-sidebar {
            position: sticky;
            top: 80px;
            max-height: calc(100vh - 100px);
            display: flex;
            flex-direction: column;
            gap: 0.75rem;
        }

        .sidebar-search {
            width: 100%;
            padding: 0.5rem 0.75rem;
            border: 1px solid var(--border);
            border-radius: 8px;
            font-size: 0.8rem;
            background: var(--input-bg);
            color: var(--text);
        }

        .sidebar-search:focus {
            outline: none;
            border-color: var(--primary);
        }

        .sidebar-filter {
            width: 100%;
            padding: 0.4rem 0.5rem;
            border: 1px solid var(--border);
            border-radius: 8px;
            font-size: 0.8rem;
            background: var(--input-bg);
            color: var(--text);
        }

        .task-list-container {
            flex: 1;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 2px;
        }

        .task-list-item {
            padding: 0.4rem 0.6rem;
            border-radius: 6px;
            cursor: pointer;
            font-size: 0.75rem;
            border-left: 3px solid transparent;
            transition: all 0.15s;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .task-list-item:hover {
            background: var(--gray-100);
        }

        .task-list-item.current {
            background: var(--primary-light);
            color: white;
            border-left-color: var(--primary);
        }

        .task-list-item.complete {
            border-left-color: var(--success);
        }

        .task-list-item.complete::before {
            content: '✓ ';
            color: var(--success);
        }

        .task-list-item.current.complete::before {
            color: white;
        }

        .task-list-item.selected {
            background: var(--primary);
            color: white;
            opacity: 0.85;
        }

        .task-list-item.selected::before {
            content: '☑ ';
            color: white;
        }

        .batch-bar {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            flex-wrap: wrap;
            font-size: 0.8rem;
        }

        .batch-toggle {
            display: flex;
            align-items: center;
            gap: 0.3rem;
            cursor: pointer;
            font-size: 0.75rem;
            color: var(--text-secondary);
        }

        .batch-actions {
            display: flex;
            gap: 0.3rem;
        }

        .batch-action-btn {
            padding: 0.2rem 0.5rem;
            font-size: 0.7rem;
            border: 1px solid var(--border);
            border-radius: 4px;
            background: var(--input-bg);
            color: var(--text);
            cursor: pointer;
        }

        .batch-action-btn:hover {
            background: var(--gray-100);
        }

        .sidebar-pagination {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 0.5rem;
            font-size: 0.75rem;
            color: var(--gray-500);
            flex-wrap: wrap;
        }

        .sidebar-pagination button {
            padding: 0.25rem 0.5rem;
            border: 1px solid var(--gray-200);
            border-radius: 4px;
            background: white;
            cursor: pointer;
            font-size: 0.7rem;
        }

        .sidebar-pagination button:disabled {
            opacity: 0.4;
            cursor: not-allowed;
        }

        /* Task Card */
        .task-card {
            background: var(--card-bg);
            border-radius: 12px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            padding: 1.5rem;
        }

        .task-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 1rem;
            padding-bottom: 1rem;
            border-bottom: 1px solid var(--border);
        }

        .task-id {
            font-size: 0.875rem;
            color: var(--text-secondary);
        }

        .task-type {
            background: var(--primary-light);
            color: white;
            padding: 0.25rem 0.75rem;
            border-radius: 9999px;
            font-size: 0.75rem;
            font-weight: 500;
        }

        .task-field {
            margin-bottom: 1rem;
        }

        .task-field-label {
            font-size: 0.75rem;
            font-weight: 600;
            color: var(--text-secondary);
            text-transform: uppercase;
            margin-bottom: 0.5rem;
        }

        .task-field-value {
            background: var(--gray-50);
            padding: 1rem;
            border-radius: 8px;
            font-family: 'Monaco', 'Menlo', monospace;
            font-size: 0.875rem;
            white-space: pre-wrap;
            word-break: break-word;
            max-height: 300px;
            overflow-y: auto;
        }

        .task-field-value.svg-preview {
            display: flex;
            justify-content: center;
            align-items: center;
            background: var(--card-bg);
            border: 1px solid var(--border);
        }

        .task-field-value.svg-preview svg {
            max-width: 100%;
            max-height: 200px;
        }

        /* Annotation Panel */
        .annotation-panel {
            background: var(--card-bg);
            border-radius: 12px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            padding: 1.5rem;
            position: sticky;
            top: 80px;
        }

        .annotation-panel h3 {
            font-size: 1rem;
            margin-bottom: 1rem;
        }

        /* Score & Choice Buttons (shared styles) */
        .score-buttons, .choice-buttons {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
            margin-bottom: 1.5rem;
        }

        .score-btn, .choice-btn {
            padding: 0.75rem 1rem;
            border: 2px solid var(--border);
            border-radius: 8px;
            background: var(--card-bg);
            cursor: pointer;
            font-size: 0.875rem;
            text-align: left;
            transition: all 0.2s;
            color: var(--text);
        }

        .score-btn:hover, .choice-btn:hover {
            border-color: var(--primary-light);
        }

        .score-btn.selected, .choice-btn.selected {
            border-color: var(--primary);
            background: var(--primary);
            color: white;
        }

        .score-btn[data-score="1"] { border-left: 4px solid var(--success); }
        .score-btn[data-score="0.5"] { border-left: 4px solid var(--warning); }
        .score-btn[data-score="0"] { border-left: 4px solid var(--danger); }

        /* Text Annotation */
        .text-annotation {
            margin-bottom: 1.5rem;
        }

        .text-annotation textarea {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid var(--border);
            border-radius: 8px;
            font-size: 0.875rem;
            resize: vertical;
            min-height: 120px;
            font-family: inherit;
            background: var(--input-bg);
            color: var(--text);
        }

        .text-annotation textarea:focus {
            outline: none;
            border-color: var(--primary);
        }

        .text-annotation .char-count {
            font-size: 0.75rem;
            color: var(--text-secondary);
            text-align: right;
            margin-top: 0.25rem;
        }

        /* Ranking */
        .ranking-list {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
            margin-bottom: 1.5rem;
        }

        .ranking-item {
            display: flex;
            align-items: center;
            gap: 0.75rem;
            padding: 0.75rem 1rem;
            border: 2px solid var(--border);
            border-radius: 8px;
            background: var(--card-bg);
            cursor: grab;
            font-size: 0.875rem;
            transition: all 0.2s;
            user-select: none;
        }

        .ranking-item:active {
            cursor: grabbing;
        }

        .ranking-item.dragging {
            opacity: 0.5;
            border-color: var(--primary);
        }

        .ranking-item .rank-number {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            background: var(--primary);
            color: white;
            font-size: 0.75rem;
            font-weight: 600;
            flex-shrink: 0;
        }

        .ranking-item .drag-handle {
            color: var(--gray-300);
            flex-shrink: 0;
        }

        .comment-area {
            margin-bottom: 1.5rem;
        }

        .comment-area label {
            display: block;
            font-size: 0.875rem;
            font-weight: 500;
            margin-bottom: 0.5rem;
        }

        .comment-area textarea {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid var(--border);
            border-radius: 8px;
            font-size: 0.875rem;
            resize: vertical;
            min-height: 80px;
            background: var(--input-bg);
            color: var(--text);
        }

        .comment-area textarea:focus {
            outline: none;
            border-color: var(--primary);
        }

        /* Navigation */
        .nav-buttons {
            display: flex;
            gap: 0.5rem;
        }

        .nav-btn {
            flex: 1;
            padding: 0.75rem;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.875rem;
            font-weight: 500;
            transition: all 0.2s;
        }

        .nav-btn.primary {
            background: var(--primary);
            color: white;
        }

        .nav-btn.primary:hover {
            background: var(--primary-light);
        }

        .nav-btn.secondary {
            background: var(--gray-100);
            color: var(--text);
        }

        .nav-btn.secondary:hover {
            background: var(--gray-200);
        }

        .nav-btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        /* Guidelines Panel */
        .guidelines-panel {
            margin-top: 2rem;
        }

        .guidelines-toggle {
            width: 100%;
            padding: 0.75rem 1rem;
            background: var(--gray-100);
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.875rem;
            text-align: left;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .guidelines-content {
            display: none;
            padding: 1rem;
            background: var(--card-bg);
            border: 1px solid var(--border);
            border-radius: 0 0 8px 8px;
            font-size: 0.875rem;
            max-height: 400px;
            overflow-y: auto;
        }

        .guidelines-content.open {
            display: block;
        }

        .guidelines-content h1, .guidelines-content h2, .guidelines-content h3 {
            margin-top: 1rem;
            margin-bottom: 0.5rem;
        }

        .guidelines-content table {
            width: 100%;
            border-collapse: collapse;
            margin: 1rem 0;
        }

        .guidelines-content th, .guidelines-content td {
            border: 1px solid var(--border);
            padding: 0.5rem;
            text-align: left;
        }

        .guidelines-content code {
            background: var(--gray-100);
            padding: 0.125rem 0.375rem;
            border-radius: 4px;
            font-size: 0.8125rem;
        }

        /* Export Button */
        .export-section {
            margin-top: 1.5rem;
            padding-top: 1.5rem;
            border-top: 1px solid var(--border);
        }

        .export-btn {
            width: 100%;
            padding: 0.75rem;
            background: var(--success);
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.875rem;
            font-weight: 500;
        }

        .export-btn:hover {
            opacity: 0.9;
        }

        /* Status indicator */
        .status-indicator {
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            margin-right: 0.5rem;
        }

        .status-indicator.complete { background: var(--success); }
        .status-indicator.partial { background: var(--warning); }
        .status-indicator.pending { background: var(--gray-300); }

        /* Keyboard shortcuts hint */
        .shortcuts-hint {
            font-size: 0.75rem;
            color: var(--text-secondary);
            margin-top: 1rem;
            text-align: center;
        }

        .shortcuts-hint kbd {
            background: var(--gray-100);
            padding: 0.125rem 0.375rem;
            border-radius: 4px;
            border: 1px solid var(--border);
        }

        /* Undo button */
        .undo-btn {
            width: 100%;
            padding: 0.5rem;
            background: none;
            border: 1px solid var(--border);
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.8rem;
            color: var(--text-secondary);
            margin-top: 0.5rem;
            transition: all 0.2s;
        }

        .undo-btn:hover:not(:disabled) {
            background: var(--gray-100);
            color: var(--text);
        }

        .undo-btn:disabled {
            opacity: 0.3;
            cursor: not-allowed;
        }

        /* Statistics Panel */
        .stats-panel {
            margin-top: 1rem;
            padding-top: 1rem;
            border-top: 1px solid var(--border);
        }

        .stats-panel h4 {
            font-size: 0.8rem;
            color: var(--text-secondary);
            margin-bottom: 0.75rem;
        }

        .stats-row {
            display: flex;
            justify-content: space-between;
            font-size: 0.8rem;
            margin-bottom: 0.35rem;
            color: var(--text);
        }

        .stats-row .label {
            color: var(--text-secondary);
        }

        .stats-bar {
            width: 100%;
            height: 6px;
            background: var(--gray-200);
            border-radius: 3px;
            margin-top: 0.5rem;
            overflow: hidden;
        }

        .stats-bar-fill {
            height: 100%;
            border-radius: 3px;
            transition: width 0.3s;
        }

        .stats-distribution {
            display: flex;
            gap: 2px;
            align-items: flex-end;
            height: 40px;
            margin-top: 0.5rem;
        }

        .stats-dist-bar {
            flex: 1;
            background: var(--primary-light);
            border-radius: 2px 2px 0 0;
            min-height: 2px;
            position: relative;
        }

        .stats-dist-bar .dist-label {
            position: absolute;
            bottom: -1.2rem;
            left: 50%;
            transform: translateX(-50%);
            font-size: 0.6rem;
            color: var(--text-secondary);
            white-space: nowrap;
        }

        /* Modal */
        .modal-overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 2000;
            justify-content: center;
            align-items: center;
        }

        .modal-overlay.open {
            display: flex;
        }

        .modal {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 2rem;
            max-width: 480px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
        }

        .modal h3 {
            margin-bottom: 1rem;
            font-size: 1.1rem;
        }

        .modal table {
            width: 100%;
            font-size: 0.85rem;
        }

        .modal td {
            padding: 0.4rem 0.5rem;
            border-bottom: 1px solid var(--border);
        }

        .modal td:first-child {
            white-space: nowrap;
        }

        .modal td kbd {
            background: var(--gray-100);
            padding: 0.15rem 0.4rem;
            border-radius: 4px;
            border: 1px solid var(--border);
            font-size: 0.8rem;
        }

        .modal-close {
            margin-top: 1rem;
            width: 100%;
            padding: 0.6rem;
            background: var(--gray-100);
            border: none;
            border-radius: 8px;
            cursor: pointer;
            color: var(--text);
            font-size: 0.875rem;
        }

        .modal-close:hover {
            background: var(--gray-200);
        }

        /* Toast notification */
        .toast {
            position: fixed;
            bottom: 2rem;
            right: 2rem;
            background: var(--gray-900);
            color: white;
            padding: 0.75rem 1.5rem;
            border-radius: 8px;
            font-size: 0.875rem;
            opacity: 0;
            transform: translateY(1rem);
            transition: all 0.3s;
            z-index: 1000;
        }

        [data-theme="dark"] .toast {
            background: var(--gray-100);
            color: var(--text);
        }

        .toast.show {
            opacity: 1;
            transform: translateY(0);
        }
    </style>
</head>
<body>
    <header>
        <h1>{{ title }}</h1>
        <div class="header-right">
            <div class="progress-info">
                <div class="progress-bar">
                    <div class="progress-bar-fill" id="progressBar"></div>
                </div>
                <span class="progress-text" id="progressText">0 / {{ total_tasks }}</span>
            </div>
            <button class="theme-toggle" id="themeToggle" onclick="toggleTheme()" title="切换暗黑模式">🌙</button>
        </div>
    </header>

    <main>
        <div class="task-sidebar" id="taskSidebar">
            <input type="text" class="sidebar-search" id="sidebarSearch" placeholder="搜索任务..." oninput="filterTaskList()">
            <select class="sidebar-filter" id="sidebarFilter" onchange="filterTaskList()">
                <option value="all">全部</option>
                <option value="completed">已完成</option>
                <option value="pending">未完成</option>
            </select>
            <select class="sidebar-filter" id="sidebarValueFilter" onchange="filterTaskList()" style="display:none">
                <option value="">按标注值筛选</option>
            </select>
            <div class="batch-bar" id="batchBar">
                <label class="batch-toggle">
                    <input type="checkbox" id="batchMode" onchange="toggleBatchMode()"> 批量模式
                </label>
                <span id="batchCount" style="display:none;font-size:0.7rem;color:var(--primary)">已选 0</span>
                <div class="batch-actions" id="batchActions" style="display:none">
                    <button class="batch-action-btn" onclick="batchSelectAll()">全选</button>
                    <button class="batch-action-btn" onclick="batchDeselectAll()">清除</button>
                    <button class="batch-action-btn" onclick="batchExport()">导出选中</button>
                </div>
            </div>
            <div class="task-list-container" id="taskListContainer"></div>
            <div class="sidebar-pagination" id="sidebarPagination"></div>
        </div>

        <div class="task-area">
            <div class="task-card" id="taskCard">
                <div class="task-header">
                    <span class="task-id" id="taskId">TASK_001</span>
                    <span class="task-type" id="taskType">default</span>
                </div>
                <div id="taskFields">
                    <!-- Task fields will be rendered here -->
                </div>
            </div>

            <div class="guidelines-panel">
                <button class="guidelines-toggle" onclick="toggleGuidelines()">
                    <span>📖 标注指南</span>
                    <span id="guidelinesArrow">▼</span>
                </button>
                <div class="guidelines-content" id="guidelinesContent">
                    {{ guidelines_html | safe }}
                </div>
            </div>
        </div>

        <div class="annotation-area">
            <div class="annotation-panel">
                <h3 id="annotationTitle">标注</h3>

                <!-- Annotation widget container (rendered by JS based on type) -->
                <div id="annotationWidget"></div>

                <div class="comment-area">
                    <label for="comment">备注 (可选)</label>
                    <textarea id="comment" placeholder="添加备注..."></textarea>
                </div>

                <div class="nav-buttons">
                    <button class="nav-btn secondary" id="prevBtn" onclick="prevTask()">
                        ← 上一条
                    </button>
                    <button class="nav-btn primary" id="nextBtn" onclick="nextTask()">
                        下一条 →
                    </button>
                </div>

                <button class="undo-btn" id="undoBtn" onclick="undoAnnotation()" disabled>
                    ↩ 撤销 (Ctrl+Z)
                </button>

                <div class="shortcuts-hint" id="shortcutsHint">
                    快捷键: <kbd>←</kbd> <kbd>→</kbd> 导航 · <kbd>?</kbd> 帮助
                </div>

                <div class="stats-panel" id="statsPanel">
                    <h4>统计</h4>
                    <div class="stats-row"><span class="label">已完成</span><span id="statsCompleted">0</span></div>
                    <div class="stats-row"><span class="label">未完成</span><span id="statsPending">0</span></div>
                    <div class="stats-row"><span class="label">完成率</span><span id="statsRate">0%</span></div>
                    <div class="stats-bar"><div class="stats-bar-fill" id="statsBar" style="width:0;background:var(--success)"></div></div>
                    <div id="statsDistribution"></div>
                </div>

                <div class="export-section">
                    <div style="display:flex;gap:0.5rem;align-items:center">
                        <select id="exportFormat" style="padding:0.6rem;border:1px solid var(--border);border-radius:8px;background:var(--input-bg);color:var(--text);font-size:0.8rem">
                            <option value="json">JSON</option>
                            <option value="jsonl">JSONL</option>
                            <option value="csv">CSV</option>
                        </select>
                        <button class="export-btn" onclick="exportResults()" style="flex:1">
                            📥 导出标注结果
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </main>

    <div class="modal-overlay" id="shortcutModal" onclick="closeShortcutModal(event)">
        <div class="modal">
            <h3>快捷键帮助</h3>
            <table>
                <tr><td><kbd>←</kbd></td><td>上一条任务</td></tr>
                <tr><td><kbd>→</kbd></td><td>下一条任务</td></tr>
                <tr><td><kbd>1</kbd>-<kbd>9</kbd></td><td>选择评分/选项</td></tr>
                <tr><td><kbd>Ctrl</kbd>+<kbd>Z</kbd></td><td>撤销当前任务标注</td></tr>
                <tr><td><kbd>?</kbd></td><td>打开/关闭此帮助</td></tr>
            </table>
            <button class="modal-close" onclick="document.getElementById('shortcutModal').classList.remove('open')">关闭</button>
        </div>
    </div>

    <div class="toast" id="toast"></div>

    <script>
        // Embedded data
        const TASKS = {{ tasks_json | safe }};
        const SCHEMA = {{ schema_json | safe }};
        const TOTAL_TASKS = {{ total_tasks }};
        const ANNOTATION_TYPE = '{{ annotation_type }}';
        const ANNOTATION_CONFIG = {{ annotation_config_json | safe }};
        let pageSize = {{ page_size }};

        // State
        let currentIndex = 0;
        let responses = {};
        let sidebarPage = 0;
        let filteredIndices = [...Array(TASKS.length).keys()];
        let undoHistory = {};  // taskId -> previous response (or null)
        let batchMode = false;
        let selectedTasks = new Set();

        // Load saved responses from localStorage
        const storageKey = 'datalabel_' + (SCHEMA.project_name || 'default').replace(/\s+/g, '_');
        const savedResponses = localStorage.getItem(storageKey);
        if (savedResponses) {
            try {
                responses = JSON.parse(savedResponses);
            } catch (e) {
                console.error('Failed to load saved responses:', e);
            }
        }

        // Initialize
        document.addEventListener('DOMContentLoaded', () => {
            initTheme();
            initAnnotationWidget();
            initValueFilter();
            initPageSizeSelect();
            renderTask(currentIndex);
            updateProgress();
            updateStats();
            renderTaskList();
        });

        // Keyboard shortcuts
        document.addEventListener('keydown', (e) => {
            // Ctrl+Z works everywhere
            if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
                e.preventDefault();
                undoAnnotation();
                return;
            }

            if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') return;

            switch(e.key) {
                case 'ArrowLeft':
                    prevTask();
                    break;
                case 'ArrowRight':
                    nextTask();
                    break;
                case '?':
                    toggleShortcutModal();
                    break;
                default:
                    handleAnnotationShortcut(e.key);
                    break;
            }
        });

        // ==================== Annotation Widget ====================

        function initAnnotationWidget() {
            const widget = document.getElementById('annotationWidget');
            const title = document.getElementById('annotationTitle');
            const hints = document.getElementById('shortcutsHint');

            if (ANNOTATION_TYPE === 'scoring') {
                title.textContent = '评分';
                hints.innerHTML = '快捷键: <kbd>←</kbd> <kbd>→</kbd> 导航 · <kbd>1</kbd> <kbd>2</kbd> <kbd>3</kbd> 评分';
                renderScoringWidget(widget);
            } else if (ANNOTATION_TYPE === 'single_choice') {
                title.textContent = '选择';
                hints.innerHTML = '快捷键: <kbd>←</kbd> <kbd>→</kbd> 导航 · <kbd>1</kbd>-<kbd>9</kbd> 选择';
                renderChoiceWidget(widget, false);
            } else if (ANNOTATION_TYPE === 'multi_choice') {
                title.textContent = '多选';
                hints.innerHTML = '快捷键: <kbd>←</kbd> <kbd>→</kbd> 导航 · <kbd>1</kbd>-<kbd>9</kbd> 切换选项';
                renderChoiceWidget(widget, true);
            } else if (ANNOTATION_TYPE === 'text') {
                title.textContent = '文本标注';
                hints.innerHTML = '快捷键: <kbd>←</kbd> <kbd>→</kbd> 导航';
                renderTextWidget(widget);
            } else if (ANNOTATION_TYPE === 'ranking') {
                title.textContent = '排序';
                hints.innerHTML = '快捷键: <kbd>←</kbd> <kbd>→</kbd> 导航 · 拖拽排序';
                renderRankingWidget(widget);
            }
        }

        // ---------- Scoring ----------

        function renderScoringWidget(container) {
            const rubric = SCHEMA.scoring_rubric || [];
            let html = '<div class="score-buttons" id="scoreButtons">';

            if (rubric.length > 0) {
                rubric.forEach(r => {
                    const desc = r.description || r.criteria || r.label || '';
                    html += `<button class="score-btn" data-score="${r.score}" onclick="selectScore('${r.score}')">
                        ${r.score} - ${desc}
                    </button>`;
                });
            } else {
                html += `<button class="score-btn" data-score="1" onclick="selectScore('1')">✓ 正确</button>`;
                html += `<button class="score-btn" data-score="0.5" onclick="selectScore('0.5')">△ 部分正确</button>`;
                html += `<button class="score-btn" data-score="0" onclick="selectScore('0')">✗ 错误</button>`;
            }

            html += '</div>';
            container.innerHTML = html;
        }

        function selectScore(score) {
            if (score === null) return;
            document.querySelectorAll('.score-btn').forEach(btn => {
                btn.classList.remove('selected');
                if (btn.dataset.score === String(score)) {
                    btn.classList.add('selected');
                }
            });
            saveCurrentResponse();
        }

        function restoreScoring(saved) {
            document.querySelectorAll('.score-btn').forEach(btn => {
                btn.classList.remove('selected');
                if (saved && btn.dataset.score === String(saved.score)) {
                    btn.classList.add('selected');
                }
            });
        }

        function getScoringValue() {
            const btn = document.querySelector('.score-btn.selected');
            return btn ? { score: parseFloat(btn.dataset.score) } : null;
        }

        // ---------- Single/Multi Choice ----------

        function renderChoiceWidget(container, multi) {
            const options = (ANNOTATION_CONFIG.options || []);
            let html = '<div class="choice-buttons" id="choiceButtons">';

            options.forEach((opt, i) => {
                const value = opt.value || opt.label;
                const label = opt.label || opt.value;
                html += `<button class="choice-btn" data-value="${value}" onclick="selectChoice('${value}', ${multi})">
                    ${i + 1}. ${label}
                </button>`;
            });

            html += '</div>';
            container.innerHTML = html;
        }

        function selectChoice(value, multi) {
            if (multi) {
                const btn = document.querySelector(`.choice-btn[data-value="${value}"]`);
                if (btn) btn.classList.toggle('selected');
            } else {
                document.querySelectorAll('.choice-btn').forEach(btn => {
                    btn.classList.remove('selected');
                    if (btn.dataset.value === value) {
                        btn.classList.add('selected');
                    }
                });
            }
            saveCurrentResponse();
        }

        function restoreChoice(saved) {
            document.querySelectorAll('.choice-btn').forEach(btn => btn.classList.remove('selected'));
            if (!saved) return;

            if (ANNOTATION_TYPE === 'single_choice' && saved.choice) {
                const btn = document.querySelector(`.choice-btn[data-value="${saved.choice}"]`);
                if (btn) btn.classList.add('selected');
            } else if (ANNOTATION_TYPE === 'multi_choice' && saved.choices) {
                saved.choices.forEach(val => {
                    const btn = document.querySelector(`.choice-btn[data-value="${val}"]`);
                    if (btn) btn.classList.add('selected');
                });
            }
        }

        function getChoiceValue() {
            if (ANNOTATION_TYPE === 'single_choice') {
                const btn = document.querySelector('.choice-btn.selected');
                return btn ? { choice: btn.dataset.value } : null;
            } else {
                const selected = Array.from(document.querySelectorAll('.choice-btn.selected'))
                    .map(btn => btn.dataset.value);
                return selected.length > 0 ? { choices: selected } : null;
            }
        }

        // ---------- Text ----------

        function renderTextWidget(container) {
            const placeholder = ANNOTATION_CONFIG.placeholder || '请输入标注文本...';
            const maxLength = ANNOTATION_CONFIG.max_length || 0;
            let html = '<div class="text-annotation">';
            html += `<textarea id="annotationText" placeholder="${placeholder}"`;
            if (maxLength > 0) html += ` maxlength="${maxLength}"`;
            html += ` oninput="onTextInput()"></textarea>`;
            if (maxLength > 0) {
                html += `<div class="char-count"><span id="charCount">0</span> / ${maxLength}</div>`;
            }
            html += '</div>';
            container.innerHTML = html;
        }

        function onTextInput() {
            const textarea = document.getElementById('annotationText');
            const countEl = document.getElementById('charCount');
            if (countEl) countEl.textContent = textarea.value.length;
            saveCurrentResponse();
        }

        function restoreText(saved) {
            const textarea = document.getElementById('annotationText');
            if (textarea) {
                textarea.value = (saved && saved.text) || '';
                const countEl = document.getElementById('charCount');
                if (countEl) countEl.textContent = textarea.value.length;
            }
        }

        function getTextValue() {
            const textarea = document.getElementById('annotationText');
            return textarea && textarea.value.trim() ? { text: textarea.value.trim() } : null;
        }

        // ---------- Ranking ----------

        let rankingOrder = [];

        function renderRankingWidget(container) {
            const options = (ANNOTATION_CONFIG.options || []);
            rankingOrder = options.map(opt => opt.value || opt.label);

            let html = '<div class="ranking-list" id="rankingList">';
            rankingOrder.forEach((value, i) => {
                const label = options.find(o => (o.value || o.label) === value)?.label || value;
                html += `<div class="ranking-item" draggable="true" data-value="${value}">
                    <span class="drag-handle">☰</span>
                    <span class="rank-number">${i + 1}</span>
                    <span>${label}</span>
                </div>`;
            });
            html += '</div>';
            container.innerHTML = html;

            initDragAndDrop();
        }

        function initDragAndDrop() {
            const list = document.getElementById('rankingList');
            if (!list) return;

            let draggedItem = null;

            list.addEventListener('dragstart', (e) => {
                draggedItem = e.target.closest('.ranking-item');
                if (draggedItem) {
                    draggedItem.classList.add('dragging');
                    e.dataTransfer.effectAllowed = 'move';
                }
            });

            list.addEventListener('dragend', (e) => {
                if (draggedItem) {
                    draggedItem.classList.remove('dragging');
                    draggedItem = null;
                    updateRankNumbers();
                    saveCurrentResponse();
                }
            });

            list.addEventListener('dragover', (e) => {
                e.preventDefault();
                const afterElement = getDragAfterElement(list, e.clientY);
                if (draggedItem) {
                    if (afterElement) {
                        list.insertBefore(draggedItem, afterElement);
                    } else {
                        list.appendChild(draggedItem);
                    }
                }
            });
        }

        function getDragAfterElement(container, y) {
            const elements = [...container.querySelectorAll('.ranking-item:not(.dragging)')];
            return elements.reduce((closest, child) => {
                const box = child.getBoundingClientRect();
                const offset = y - box.top - box.height / 2;
                if (offset < 0 && offset > closest.offset) {
                    return { offset: offset, element: child };
                }
                return closest;
            }, { offset: Number.NEGATIVE_INFINITY }).element;
        }

        function updateRankNumbers() {
            const items = document.querySelectorAll('.ranking-item');
            rankingOrder = [];
            items.forEach((item, i) => {
                item.querySelector('.rank-number').textContent = i + 1;
                rankingOrder.push(item.dataset.value);
            });
        }

        function restoreRanking(saved) {
            if (!saved || !saved.ranking) {
                // Reset to default order
                const options = (ANNOTATION_CONFIG.options || []);
                rankingOrder = options.map(opt => opt.value || opt.label);
            } else {
                rankingOrder = saved.ranking;
            }

            const list = document.getElementById('rankingList');
            if (!list) return;

            // Reorder items
            rankingOrder.forEach(value => {
                const item = list.querySelector(`.ranking-item[data-value="${value}"]`);
                if (item) list.appendChild(item);
            });
            updateRankNumbers();
        }

        function getRankingValue() {
            return rankingOrder.length > 0 ? { ranking: [...rankingOrder] } : null;
        }

        // ==================== Common Functions ====================

        function handleAnnotationShortcut(key) {
            const num = parseInt(key);
            if (isNaN(num) || num < 1) return;

            if (ANNOTATION_TYPE === 'scoring') {
                const btns = document.querySelectorAll('.score-btn');
                if (btns[num - 1]) {
                    selectScore(btns[num - 1].dataset.score);
                }
            } else if (ANNOTATION_TYPE === 'single_choice' || ANNOTATION_TYPE === 'multi_choice') {
                const btns = document.querySelectorAll('.choice-btn');
                if (btns[num - 1]) {
                    selectChoice(btns[num - 1].dataset.value, ANNOTATION_TYPE === 'multi_choice');
                }
            }
        }

        function renderTask(index) {
            if (index < 0 || index >= TASKS.length) return;

            const task = TASKS[index];
            const taskId = task.id || `TASK_${String(index + 1).padStart(3, '0')}`;

            // Update header
            document.getElementById('taskId').textContent = taskId;
            document.getElementById('taskType').textContent = task.task_type || 'default';

            // Render fields
            const fieldsContainer = document.getElementById('taskFields');
            fieldsContainer.innerHTML = '';

            const data = task.data || task;
            for (const [key, value] of Object.entries(data)) {
                if (key === 'task_type' || key === 'id') continue;

                const fieldDiv = document.createElement('div');
                fieldDiv.className = 'task-field';

                const labelDiv = document.createElement('div');
                labelDiv.className = 'task-field-label';
                labelDiv.textContent = key;

                const valueDiv = document.createElement('div');
                valueDiv.className = 'task-field-value';

                // Check if it's SVG content
                if (typeof value === 'string' && value.trim().startsWith('<svg')) {
                    valueDiv.className += ' svg-preview';
                    valueDiv.innerHTML = value;
                } else if (typeof value === 'object') {
                    valueDiv.textContent = JSON.stringify(value, null, 2);
                } else {
                    valueDiv.textContent = value;
                }

                fieldDiv.appendChild(labelDiv);
                fieldDiv.appendChild(valueDiv);
                fieldsContainer.appendChild(fieldDiv);
            }

            // Restore saved response
            const saved = responses[taskId];
            restoreAnnotation(saved);
            document.getElementById('comment').value = (saved && saved.comment) || '';

            // Update navigation buttons
            document.getElementById('prevBtn').disabled = index === 0;
            document.getElementById('nextBtn').textContent = index === TASKS.length - 1 ? '完成 ✓' : '下一条 →';

            currentIndex = index;
            updateUndoBtn(taskId);
            renderTaskList();
        }

        function restoreAnnotation(saved) {
            if (ANNOTATION_TYPE === 'scoring') {
                restoreScoring(saved);
            } else if (ANNOTATION_TYPE === 'single_choice' || ANNOTATION_TYPE === 'multi_choice') {
                restoreChoice(saved);
            } else if (ANNOTATION_TYPE === 'text') {
                restoreText(saved);
            } else if (ANNOTATION_TYPE === 'ranking') {
                restoreRanking(saved);
            }
        }

        function getAnnotationValue() {
            if (ANNOTATION_TYPE === 'scoring') return getScoringValue();
            if (ANNOTATION_TYPE === 'single_choice' || ANNOTATION_TYPE === 'multi_choice') return getChoiceValue();
            if (ANNOTATION_TYPE === 'text') return getTextValue();
            if (ANNOTATION_TYPE === 'ranking') return getRankingValue();
            return null;
        }

        function saveCurrentResponse() {
            const task = TASKS[currentIndex];
            const taskId = task.id || `TASK_${String(currentIndex + 1).padStart(3, '0')}`;

            const value = getAnnotationValue();
            const comment = document.getElementById('comment').value.trim();

            if (value !== null) {
                // Save undo history
                undoHistory[taskId] = responses[taskId] ? { ...responses[taskId] } : null;

                responses[taskId] = {
                    task_id: taskId,
                    ...value,
                    comment: comment,
                    annotated_at: new Date().toISOString(),
                };

                localStorage.setItem(storageKey, JSON.stringify(responses));
                updateProgress();
                updateStats();
                updateUndoBtn(taskId);
                showToast('已保存');
            }
        }

        function prevTask() {
            saveCurrentResponse();
            if (currentIndex > 0) {
                renderTask(currentIndex - 1);
            }
        }

        function nextTask() {
            saveCurrentResponse();
            if (currentIndex < TASKS.length - 1) {
                renderTask(currentIndex + 1);
            } else {
                showToast('已完成所有任务!');
            }
        }

        function updateProgress() {
            const completed = Object.keys(responses).length;
            const percentage = (completed / TOTAL_TASKS) * 100;

            document.getElementById('progressBar').style.width = percentage + '%';
            document.getElementById('progressText').textContent = `${completed} / ${TOTAL_TASKS}`;
        }

        function toggleGuidelines() {
            const content = document.getElementById('guidelinesContent');
            const arrow = document.getElementById('guidelinesArrow');

            content.classList.toggle('open');
            arrow.textContent = content.classList.contains('open') ? '▲' : '▼';
        }

        function exportResults() {
            const format = document.getElementById('exportFormat').value;
            const resp = Object.values(responses);
            const baseName = (SCHEMA.project_name || 'annotation').replace(/\s+/g, '_');
            let content, mimeType, ext;

            if (format === 'jsonl') {
                content = resp.map(r => JSON.stringify(r)).join('\n');
                mimeType = 'application/x-jsonlines';
                ext = 'jsonl';
            } else if (format === 'csv') {
                content = exportAsCSV(resp);
                mimeType = 'text/csv';
                ext = 'csv';
            } else {
                const exportData = {
                    schema: SCHEMA,
                    metadata: {
                        exported_at: new Date().toISOString(),
                        total_tasks: TOTAL_TASKS,
                        completed_tasks: resp.length,
                        annotation_type: ANNOTATION_TYPE,
                        tool: 'DataLabel',
                        version: '0.1.0',
                    },
                    responses: resp,
                };
                content = JSON.stringify(exportData, null, 2);
                mimeType = 'application/json';
                ext = 'json';
            }

            const blob = new Blob([content], { type: mimeType });
            const url = URL.createObjectURL(blob);

            const a = document.createElement('a');
            a.href = url;
            a.download = `${baseName}_results.${ext}`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);

            showToast('导出成功!');
        }

        function exportAsCSV(rows) {
            if (rows.length === 0) return '';

            // Collect all keys
            const keys = new Set();
            rows.forEach(r => Object.keys(r).forEach(k => keys.add(k)));
            const headers = [...keys];

            const escape = (val) => {
                if (val === null || val === undefined) return '';
                const s = typeof val === 'object' ? JSON.stringify(val) : String(val);
                if (s.includes(',') || s.includes('"') || s.includes('\n')) {
                    return '"' + s.replace(/"/g, '""') + '"';
                }
                return s;
            };

            const lines = [headers.map(escape).join(',')];
            rows.forEach(r => {
                lines.push(headers.map(h => escape(r[h])).join(','));
            });
            return lines.join('\n');
        }

        // ==================== Task Sidebar ====================

        function filterTaskList() {
            const query = (document.getElementById('sidebarSearch')?.value || '').toLowerCase();
            const filter = document.getElementById('sidebarFilter')?.value || 'all';
            const valueFilter = document.getElementById('sidebarValueFilter')?.value || '';

            filteredIndices = [];
            for (let i = 0; i < TASKS.length; i++) {
                const task = TASKS[i];
                const taskId = task.id || `TASK_${String(i + 1).padStart(3, '0')}`;
                const isComplete = !!responses[taskId];

                // Filter by status
                if (filter === 'completed' && !isComplete) continue;
                if (filter === 'pending' && isComplete) continue;

                // Filter by annotation value
                if (valueFilter) {
                    const resp = responses[taskId];
                    if (!resp) continue;
                    if (ANNOTATION_TYPE === 'scoring' && String(resp.score) !== valueFilter) continue;
                    if (ANNOTATION_TYPE === 'single_choice' && resp.choice !== valueFilter) continue;
                    if (ANNOTATION_TYPE === 'multi_choice' && !(resp.choices || []).includes(valueFilter)) continue;
                }

                // Filter by search query
                if (query) {
                    const data = task.data || task;
                    const text = taskId + ' ' + Object.values(data).map(v => String(v)).join(' ');
                    if (!text.toLowerCase().includes(query)) continue;
                }

                filteredIndices.push(i);
            }

            sidebarPage = 0;
            renderTaskList();
        }

        function renderTaskList() {
            const container = document.getElementById('taskListContainer');
            if (!container) return;

            const start = sidebarPage * pageSize;
            const end = Math.min(start + pageSize, filteredIndices.length);
            const pageIndices = filteredIndices.slice(start, end);

            container.innerHTML = '';
            pageIndices.forEach(i => {
                const task = TASKS[i];
                const taskId = task.id || `TASK_${String(i + 1).padStart(3, '0')}`;
                const isComplete = !!responses[taskId];
                const isCurrent = (i === currentIndex);
                const isSelected = selectedTasks.has(taskId);

                const item = document.createElement('div');
                item.className = 'task-list-item';
                if (isComplete) item.className += ' complete';
                if (isCurrent) item.className += ' current';
                if (batchMode && isSelected) item.className += ' selected';
                item.textContent = taskId;
                item.title = taskId;

                if (batchMode) {
                    item.onclick = () => { toggleTaskSelection(taskId); };
                } else {
                    item.onclick = () => { renderTask(i); renderTaskList(); };
                }
                container.appendChild(item);
            });

            renderSidebarPagination();
        }

        function renderSidebarPagination() {
            const pag = document.getElementById('sidebarPagination');
            if (!pag) return;

            const totalPages = Math.ceil(filteredIndices.length / pageSize);
            const pageSizeOptions = [25, 50, 100, 200].map(n =>
                `<option value="${n}" ${n === pageSize ? 'selected' : ''}>${n}/页</option>`
            ).join('');
            const pageSizeSelect = `<select onchange="changePageSize(this.value)" style="padding:0.15rem 0.3rem;border:1px solid var(--gray-200);border-radius:4px;font-size:0.7rem;background:var(--input-bg);color:var(--text)">${pageSizeOptions}</select>`;

            if (totalPages <= 1) {
                pag.innerHTML = `<span>${filteredIndices.length} 条</span>${pageSizeSelect}`;
                return;
            }

            pag.innerHTML = `
                <button onclick="sidebarPrevPage()" ${sidebarPage === 0 ? 'disabled' : ''}>‹</button>
                <span>${sidebarPage + 1} / ${totalPages}</span>
                <button onclick="sidebarNextPage()" ${sidebarPage >= totalPages - 1 ? 'disabled' : ''}>›</button>
                ${pageSizeSelect}
            `;
        }

        function sidebarPrevPage() {
            if (sidebarPage > 0) {
                sidebarPage--;
                renderTaskList();
            }
        }

        function sidebarNextPage() {
            const totalPages = Math.ceil(filteredIndices.length / pageSize);
            if (sidebarPage < totalPages - 1) {
                sidebarPage++;
                renderTaskList();
            }
        }

        // ==================== Page Size ====================

        function changePageSize(val) {
            pageSize = parseInt(val);
            sidebarPage = 0;
            renderTaskList();
        }

        function initPageSizeSelect() {
            // Page size select is rendered inside renderSidebarPagination()
        }

        // ==================== Value Filter ====================

        function initValueFilter() {
            const sel = document.getElementById('sidebarValueFilter');
            if (!sel) return;

            if (ANNOTATION_TYPE === 'scoring') {
                const rubric = SCHEMA.scoring_rubric || [];
                rubric.forEach(r => {
                    const opt = document.createElement('option');
                    opt.value = String(r.score);
                    opt.textContent = `${r.label || r.description || ''} (${r.score})`;
                    sel.appendChild(opt);
                });
                sel.style.display = '';
            } else if (ANNOTATION_TYPE === 'single_choice' || ANNOTATION_TYPE === 'multi_choice') {
                const options = (ANNOTATION_CONFIG && ANNOTATION_CONFIG.options) || [];
                options.forEach(o => {
                    const opt = document.createElement('option');
                    opt.value = typeof o === 'string' ? o : o.value;
                    opt.textContent = typeof o === 'string' ? o : (o.label || o.value);
                    sel.appendChild(opt);
                });
                sel.style.display = '';
            }
            // text/ranking: keep hidden
        }

        // ==================== Batch Operations ====================

        function toggleBatchMode() {
            batchMode = document.getElementById('batchMode').checked;
            document.getElementById('batchCount').style.display = batchMode ? '' : 'none';
            document.getElementById('batchActions').style.display = batchMode ? 'flex' : 'none';
            if (!batchMode) {
                selectedTasks.clear();
            }
            updateBatchCount();
            renderTaskList();
        }

        function toggleTaskSelection(taskId) {
            if (selectedTasks.has(taskId)) {
                selectedTasks.delete(taskId);
            } else {
                selectedTasks.add(taskId);
            }
            updateBatchCount();
            renderTaskList();
        }

        function batchSelectAll() {
            filteredIndices.forEach(i => {
                const task = TASKS[i];
                const taskId = task.id || `TASK_${String(i + 1).padStart(3, '0')}`;
                selectedTasks.add(taskId);
            });
            updateBatchCount();
            renderTaskList();
        }

        function batchDeselectAll() {
            selectedTasks.clear();
            updateBatchCount();
            renderTaskList();
        }

        function updateBatchCount() {
            const el = document.getElementById('batchCount');
            if (el) el.textContent = `已选 ${selectedTasks.size}`;
        }

        function batchExport() {
            if (selectedTasks.size === 0) {
                showToast('未选择任何任务');
                return;
            }
            const format = document.getElementById('exportFormat').value;
            const resp = Object.values(responses).filter(r => selectedTasks.has(r.task_id));
            const baseName = (SCHEMA.project_name || 'annotation').replace(/\s+/g, '_');
            let content, mimeType, ext;

            if (format === 'jsonl') {
                content = resp.map(r => JSON.stringify(r)).join('\n');
                mimeType = 'application/x-jsonlines';
                ext = 'jsonl';
            } else if (format === 'csv') {
                content = exportAsCSV(resp);
                mimeType = 'text/csv';
                ext = 'csv';
            } else {
                content = JSON.stringify({
                    schema: SCHEMA,
                    metadata: {
                        exported_at: new Date().toISOString(),
                        total_tasks: TOTAL_TASKS,
                        exported_tasks: resp.length,
                        annotation_type: ANNOTATION_TYPE,
                        tool: 'DataLabel',
                        export_mode: 'batch_selected',
                    },
                    responses: resp,
                }, null, 2);
                mimeType = 'application/json';
                ext = 'json';
            }

            const blob = new Blob([content], { type: mimeType + ';charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `${baseName}_selected_${resp.length}.${ext}`;
            a.click();
            URL.revokeObjectURL(url);
            showToast(`已导出 ${resp.length} 条选中结果`);
        }

        // ==================== Theme ====================

        function initTheme() {
            const saved = localStorage.getItem('datalabel_theme');
            if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
                document.documentElement.setAttribute('data-theme', 'dark');
                document.getElementById('themeToggle').textContent = '☀️';
            }
        }

        function toggleTheme() {
            const html = document.documentElement;
            const isDark = html.getAttribute('data-theme') === 'dark';
            if (isDark) {
                html.removeAttribute('data-theme');
                document.getElementById('themeToggle').textContent = '🌙';
                localStorage.setItem('datalabel_theme', 'light');
            } else {
                html.setAttribute('data-theme', 'dark');
                document.getElementById('themeToggle').textContent = '☀️';
                localStorage.setItem('datalabel_theme', 'dark');
            }
        }

        // ==================== Undo ====================

        function updateUndoBtn(taskId) {
            const btn = document.getElementById('undoBtn');
            btn.disabled = !undoHistory.hasOwnProperty(taskId);
        }

        function undoAnnotation() {
            const task = TASKS[currentIndex];
            const taskId = task.id || `TASK_${String(currentIndex + 1).padStart(3, '0')}`;

            if (!undoHistory.hasOwnProperty(taskId)) return;

            const prev = undoHistory[taskId];
            if (prev === null) {
                delete responses[taskId];
            } else {
                responses[taskId] = prev;
            }
            delete undoHistory[taskId];

            localStorage.setItem(storageKey, JSON.stringify(responses));
            restoreAnnotation(responses[taskId] || null);
            document.getElementById('comment').value = (responses[taskId] && responses[taskId].comment) || '';
            updateProgress();
            updateStats();
            updateUndoBtn(taskId);
            renderTaskList();
            showToast('已撤销');
        }

        // ==================== Statistics ====================

        function updateStats() {
            const completed = Object.keys(responses).length;
            const pending = TOTAL_TASKS - completed;
            const rate = TOTAL_TASKS > 0 ? (completed / TOTAL_TASKS * 100) : 0;

            const el = (id) => document.getElementById(id);
            el('statsCompleted').textContent = completed;
            el('statsPending').textContent = pending;
            el('statsRate').textContent = rate.toFixed(1) + '%';
            el('statsBar').style.width = rate + '%';

            // Distribution for scoring
            const distEl = el('statsDistribution');
            if (ANNOTATION_TYPE === 'scoring' && completed > 0) {
                const counts = {};
                Object.values(responses).forEach(r => {
                    if (r.score !== undefined) {
                        const s = String(r.score);
                        counts[s] = (counts[s] || 0) + 1;
                    }
                });

                const labels = Object.keys(counts).sort();
                const maxCount = Math.max(...Object.values(counts));

                if (labels.length > 0) {
                    let html = '<div class="stats-distribution">';
                    labels.forEach(label => {
                        const h = maxCount > 0 ? (counts[label] / maxCount * 100) : 0;
                        html += `<div class="stats-dist-bar" style="height:${Math.max(h, 5)}%" title="${label}: ${counts[label]}"><span class="dist-label">${label}</span></div>`;
                    });
                    html += '</div>';
                    distEl.innerHTML = html;
                } else {
                    distEl.innerHTML = '';
                }
            } else if (ANNOTATION_TYPE === 'single_choice' && completed > 0) {
                const counts = {};
                Object.values(responses).forEach(r => {
                    if (r.choice) {
                        counts[r.choice] = (counts[r.choice] || 0) + 1;
                    }
                });

                const labels = Object.keys(counts);
                const maxCount = Math.max(...Object.values(counts));

                if (labels.length > 0) {
                    let html = '<div class="stats-distribution">';
                    labels.forEach(label => {
                        const h = maxCount > 0 ? (counts[label] / maxCount * 100) : 0;
                        const shortLabel = label.length > 4 ? label.slice(0, 4) + '..' : label;
                        html += `<div class="stats-dist-bar" style="height:${Math.max(h, 5)}%" title="${label}: ${counts[label]}"><span class="dist-label">${shortLabel}</span></div>`;
                    });
                    html += '</div>';
                    distEl.innerHTML = html;
                } else {
                    distEl.innerHTML = '';
                }
            } else {
                distEl.innerHTML = '';
            }
        }

        // ==================== Shortcut Modal ====================

        function toggleShortcutModal() {
            document.getElementById('shortcutModal').classList.toggle('open');
        }

        function closeShortcutModal(event) {
            if (event.target === event.currentTarget) {
                event.currentTarget.classList.remove('open');
            }
        }

        // ==================== Toast ====================

        function showToast(message) {
            const toast = document.getElementById('toast');
            toast.textContent = message;
            toast.classList.add('show');

            setTimeout(() => {
                toast.classList.remove('show');
            }, 2000);
        }
    </script>
</body>
</html>
