Files
liquid 7d76899a22 feat(backup-restore): add backup restoration functionality with automatic chunking support
- Introduced a new restore button in the SW debug panel for restoring data from ZIP backups.
- Implemented `BackupPartManager` to handle automatic chunking during backup processes.
- Enhanced the backup process to include tasks, prompts, and assets, with progress updates.
- Updated version to 0.6.7 and adjusted build time in `version.json`.
- Refactored existing backup logic to support the new restoration features.
2026-03-08 00:34:06 +08:00

1140 lines
63 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Worker 调试面板</title>
<link rel="stylesheet" href="./sw-debug/styles.css">
<!-- JSZip for backup functionality - local copy for reliability -->
<script src="./sw-debug/jszip.min.js"></script>
</head>
<body>
<header>
<h1>
<span class="icon">⚙️</span>
<span id="panelTitle">Service Worker 调试面板</span>
<span id="swStatus" class="status-indicator inactive">
<span class="dot"></span>
<span class="text">未连接</span>
</span>
<span id="analysisModeIndicator" class="mode-indicator analysis-mode" style="display: none;">
<span class="icon">🔍</span>
<span class="text">分析模式</span>
</span>
</h1>
<div class="header-actions">
<!-- 移动端状态面板切换按钮 -->
<button id="toggleStatusPanel" class="icon-btn mobile-only" title="显示/隐藏状态信息">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
</svg>
</button>
<!-- 模式切换按钮 -->
<button id="toggleAnalysisMode" class="icon-btn" title="切换到分析模式(导入用户日志)">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>
</svg>
</button>
<!-- 导入日志按钮 (分析模式下显示) -->
<button id="importLogs" style="display: none;">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12"/>
</svg>
导入日志
</button>
<input type="file" id="importLogsInput" accept=".json" style="display: none;">
<!-- 数据备份按钮 -->
<button id="backupData" class="success" title="备份应用数据(提示词、项目、素材)">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
<polyline points="17 21 17 13 7 13 7 21"/>
<polyline points="7 3 7 8 15 8"/>
</svg>
数据备份
</button>
<!-- 恢复备份按钮 -->
<button id="restoreBackup" class="primary" title="从备份 ZIP 恢复数据">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 15v4a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-4M7 10l5 5 5-5M12 15V3"/>
</svg>
恢复备份
</button>
<input type="file" id="restoreBackupInput" accept=".zip" style="display: none;">
<button id="toggleTheme" class="icon-btn" title="切换深色/浅色主题">
<svg id="themeIcon" class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
<button id="toggleDebug" class="danger debug-mode-only">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>
</svg>
停止调试
</button>
<button id="exportLogs" class="success">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/>
</svg>
导出日志
</button>
<!-- 设置弹窗 -->
<div class="export-modal-overlay" id="settingsModalOverlay" style="display: none;">
<div class="export-modal" style="max-width: 450px;">
<div class="export-modal-header">
<h3>⚙️ 日志设置</h3>
<button class="close-btn" id="closeSettingsModal">&times;</button>
</div>
<div class="export-modal-content" style="padding: 16px;">
<div class="setting-group">
<label class="setting-label">日志最大保留条数</label>
<select id="settingMaxLogs" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid var(--border-color);">
<option value="200">200 条</option>
<option value="500" selected>500 条(默认)</option>
<option value="1000">1000 条</option>
<option value="2000">2000 条</option>
</select>
</div>
<div class="setting-group" style="margin-top: 16px;">
<label class="setting-label">自动清理超过指定时间的日志</label>
<select id="settingAutoClean" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid var(--border-color);">
<option value="0" selected>不自动清理</option>
<option value="30">30 分钟前</option>
<option value="60">1 小时前</option>
<option value="120">2 小时前</option>
</select>
</div>
<div class="setting-group" style="margin-top: 16px;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="checkbox" id="settingKeepBookmarks" checked style="margin-right: 8px;">
自动清理时保留收藏的日志
</label>
</div>
<div style="margin-top: 20px; text-align: right;">
<button id="saveSettings" class="primary" style="padding: 8px 24px;">保存</button>
</div>
</div>
</div>
</div>
<!-- 快捷键帮助弹窗 -->
<div class="export-modal-overlay" id="shortcutsModalOverlay" style="display: none;">
<div class="export-modal" style="max-width: 400px;">
<div class="export-modal-header">
<h3>⌨️ 键盘快捷键</h3>
<button class="close-btn" id="closeShortcutsModal">&times;</button>
</div>
<div class="export-modal-content" style="padding: 16px;">
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
<tr><td style="padding: 8px 0; width: 140px;"><kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">1</kbd> - <kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">5</kbd></td><td>切换 Tab</td></tr>
<tr><td style="padding: 8px 0;"><kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">Space</kbd></td><td>暂停/恢复实时日志</td></tr>
<tr><td style="padding: 8px 0;"><kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">Ctrl</kbd>+<kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">L</kbd></td><td>清空当前 Tab 日志</td></tr>
<tr><td style="padding: 8px 0;"><kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">Esc</kbd></td><td>关闭弹窗</td></tr>
<tr><td style="padding: 8px 0;"><kbd style="background:#e0e0e0;padding:2px 6px;border-radius:3px;">?</kbd></td><td>显示此帮助</td></tr>
</table>
</div>
</div>
</div>
<!-- 导出日志弹窗 -->
<div class="export-modal-overlay" id="exportModalOverlay">
<div class="export-modal">
<div class="export-modal-header">
<h3>📥 导出日志</h3>
<button class="export-modal-close" id="closeExportModal">&times;</button>
</div>
<div class="export-modal-body">
<!-- 全选 -->
<label class="export-checkbox export-select-all">
<input type="checkbox" id="selectAllExport">
<span>全选</span>
</label>
<!-- 基本信息 -->
<div class="export-section">
<label class="export-checkbox export-section-header">
<input type="checkbox" data-section="basic" checked>
<span>基本信息</span>
</label>
<div class="export-section-items">
<label class="export-checkbox">
<input type="checkbox" name="basic" value="swStatus" checked>
<span>SW 状态</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="basic" value="memory" checked>
<span>内存信息</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="basic" value="cache" checked>
<span>缓存信息</span>
</label>
</div>
</div>
<!-- Fetch 请求日志 -->
<div class="export-section">
<label class="export-checkbox export-section-header">
<input type="checkbox" data-section="fetch">
<span>Fetch 请求日志</span>
</label>
<div class="export-section-items">
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="sw-internal">
<span>SW 内部 API</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="xhr">
<span>XHR/API</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="image">
<span>图片</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="video">
<span>视频</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="font">
<span>字体</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="static">
<span>静态资源</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="cache-url">
<span>缓存 URL</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="fetch" value="asset-library">
<span>素材库</span>
</label>
</div>
</div>
<!-- 控制台日志 -->
<div class="export-section">
<label class="export-checkbox export-section-header">
<input type="checkbox" data-section="console">
<span>控制台日志</span>
</label>
<div class="export-section-items">
<label class="export-checkbox">
<input type="checkbox" name="console" value="error">
<span>Error</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="console" value="warn">
<span>Warn</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="console" value="info">
<span>Info</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="console" value="log">
<span>Log</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="console" value="debug">
<span>Debug</span>
</label>
</div>
</div>
<!-- PostMessage 日志 -->
<div class="export-section">
<label class="export-checkbox export-section-header">
<input type="checkbox" data-section="postmessage">
<span>PostMessage 日志</span>
</label>
<div class="export-section-items">
<label class="export-checkbox">
<input type="checkbox" name="postmessage" value="receive">
<span>应用页面</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="postmessage" value="send">
<span>SW</span>
</label>
</div>
</div>
<!-- LLM API 日志 -->
<div class="export-section">
<label class="export-checkbox export-section-header">
<input type="checkbox" data-section="llmapi" checked>
<span>LLM API 日志</span>
</label>
<div class="export-section-items">
<label class="export-checkbox">
<input type="checkbox" name="llmapi" value="image" checked>
<span>图片生成</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="llmapi" value="video" checked>
<span>视频生成</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="llmapi" value="chat" checked>
<span>对话</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="llmapi" value="character" checked>
<span>角色</span>
</label>
</div>
</div>
<!-- 内存日志 -->
<div class="export-section">
<label class="export-checkbox export-section-header">
<input type="checkbox" data-section="memory">
<span>内存日志</span>
</label>
<div class="export-section-items">
<label class="export-checkbox">
<input type="checkbox" name="memory" value="startup">
<span>启动快照</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="memory" value="periodic">
<span>定期快照</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="memory" value="error">
<span>错误</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="memory" value="freeze">
<span>卡死</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="memory" value="whitescreen">
<span>白屏</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="memory" value="longtask">
<span>长任务</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="memory" value="beforeunload">
<span>页面关闭</span>
</label>
</div>
</div>
</div>
<div class="export-modal-footer">
<button id="cancelExport" class="secondary">取消</button>
<button id="doExport" class="primary">确认导出</button>
</div>
</div>
</div>
<button id="clearLogs" class="danger debug-mode-only">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>
清空日志
</button>
<button id="refreshStatus" class="debug-mode-only">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
</svg>
刷新状态
</button>
</div>
</header>
<div class="container">
<div class="panels">
<!-- Left Panel: Status & Cache -->
<div class="left-panel">
<div class="panel">
<div class="panel-header">
SW 状态信息
</div>
<div class="panel-content">
<div class="stat-grid" id="statusGrid">
<div class="stat-item">
<span class="label">版本</span>
<span class="value" id="swVersion">-</span>
</div>
<div class="stat-item">
<span class="label">调试模式</span>
<span class="value" id="debugMode">关闭</span>
</div>
<div class="stat-item">
<span class="label">Pending 图片请求</span>
<span class="value" id="pendingImages">0</span>
</div>
<div class="stat-item">
<span class="label">Pending 视频请求</span>
<span class="value" id="pendingVideos">0</span>
</div>
<div class="stat-item">
<span class="label">视频 Blob 缓存</span>
<span class="value" id="videoBlobCache">0</span>
</div>
<div class="stat-item">
<span class="label">已完成请求缓存</span>
<span class="value" id="completedRequests">0</span>
</div>
<div class="stat-item">
<span class="label">工作流处理器</span>
<span class="value" id="workflowHandler">未初始化</span>
</div>
<div class="stat-item">
<span class="label">调试日志数</span>
<span class="value" id="debugLogsCount">0</span>
</div>
</div>
<div id="failedDomainsSection" style="margin-top: 16px; display: none;">
<div class="stat-item" style="flex-direction: column; align-items: flex-start;">
<span class="label">失败域名</span>
<div class="failed-domains" id="failedDomains"></div>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">
内存监控
<span id="memoryUpdateTime" style="font-size: 11px; opacity: 0.6; margin-left: 10px;"></span>
</div>
<div class="panel-content">
<div class="stat-grid" id="memoryGrid">
<div class="stat-item">
<span class="label">JS 堆已使用</span>
<span class="value" id="memoryUsed">-</span>
</div>
<div class="stat-item">
<span class="label">JS 堆总大小</span>
<span class="value" id="memoryTotal">-</span>
</div>
<div class="stat-item">
<span class="label">JS 堆上限</span>
<span class="value" id="memoryLimit">-</span>
</div>
<div class="stat-item">
<span class="label">使用率</span>
<span class="value" id="memoryPercent">-</span>
</div>
</div>
<p id="memoryWarning" style="display: none; margin-top: 12px; padding: 8px; background: #fff3cd; border-radius: 4px; font-size: 12px; color: #856404;">
⚠️ 内存使用率较高,可能导致页面崩溃
</p>
<p id="memoryNotSupported" style="display: none; margin-top: 8px; font-size: 11px; opacity: 0.6;">
注:performance.memory 仅 Chrome 支持
</p>
</div>
</div>
<div class="panel">
<div class="panel-header">
缓存统计
<button id="refreshCache">刷新</button>
</div>
<div class="panel-content">
<ul class="cache-list" id="cacheList">
<li class="cache-item">
<span class="name">加载中...</span>
</li>
</ul>
</div>
</div>
</div>
<!-- Right Panel: Logs -->
<div class="panel logs-panel">
<div class="tabs">
<div class="tab active" data-tab="fetch">Fetch 请求日志 <span id="fetchCount" style="opacity:0.6"></span></div>
<div class="tab" data-tab="console">控制台日志 <span id="consoleCount" style="opacity:0.6">(0)</span><span id="consoleErrorDot" class="error-dot" style="display:none"></span></div>
<div class="tab" data-tab="postmessage">PostMessage日志 <span id="postmessageCount" style="opacity:0.6">(0)</span></div>
<div class="tab" data-tab="llmapi">LLM API <span id="llmapiErrorDot" class="error-dot" style="display:none"></span></div>
<div class="tab debug-mode-only" data-tab="gist">Gist 管理</div>
<div class="tab" data-tab="crash">内存日志 <span id="crashCount" style="opacity:0.6">(0)</span><span id="crashErrorDot" class="error-dot" style="display:none"></span></div>
</div>
<!-- Fetch Logs Tab -->
<div class="tab-content active" id="fetchTab">
<div class="stats-bar" id="fetchStatsBar">
<div class="stat"><span>总请求:</span> <span class="stat-value" id="statTotalRequests">0</span></div>
<div class="stat"><span>成功率:</span> <span class="stat-value" id="statSuccessRate">-</span></div>
<div class="stat"><span>平均耗时:</span> <span class="stat-value" id="statAvgDuration">-</span></div>
<div class="stat"><span>缓存命中:</span> <span class="stat-value" id="statCacheHit">-</span></div>
<div class="stat stat-clickable" id="statSlowRequestsWrapper" title="点击过滤慢请求 (>1s)"><span>慢请求:</span> <span class="stat-value" id="statSlowRequests">0</span></div>
<div class="stat stat-chart" title="耗时分布: <100ms | 100-500ms | 500ms-1s | >1s">
<span>分布:</span>
<div class="duration-chart" id="durationChart">
<div class="chart-bar fast" id="chartFast" style="width: 0%"></div>
<div class="chart-bar medium" id="chartMedium" style="width: 0%"></div>
<div class="chart-bar slow" id="chartSlow" style="width: 0%"></div>
<div class="chart-bar very-slow" id="chartVerySlow" style="width: 0%"></div>
</div>
</div>
</div>
<div class="logs-toolbar">
<select id="filterType">
<option value="">全部类型</option>
<option value="xhr">XHR/API</option>
<option value="sw-internal" selected>SW内部API</option>
<option value="image">图片</option>
<option value="video">视频</option>
<option value="font">字体</option>
<option value="static">静态资源</option>
<option value="navigation">导航</option>
<option value="cache-url">缓存URL</option>
<option value="asset-library">素材库</option>
<option value="passthrough">放行</option>
</select>
<select id="filterStatus">
<option value="">全部状态</option>
<option value="error">❌ 失败请求</option>
<option value="200">200 OK</option>
<option value="206">206 Partial</option>
<option value="304">304 Not Modified</option>
<option value="404">404 Not Found</option>
<option value="500">5xx Error</option>
<option value="slow">慢请求 (>1s)</option>
</select>
<select id="filterTimeRange">
<option value="">全部时间</option>
<option value="5">最近 5 分钟</option>
<option value="15">最近 15 分钟</option>
<option value="30">最近 30 分钟</option>
<option value="60">最近 1 小时</option>
</select>
<input type="text" id="filterUrl" placeholder="搜索 URL...">
<label class="regex-toggle" title="启用正则表达式搜索">
<input type="checkbox" id="filterUrlRegex">
<span style="font-size: 11px; margin-left: 2px;">.*</span>
</label>
<button id="togglePause" title="暂停/恢复实时更新 (快捷键: Space)">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
实时
</button>
<button id="toggleSelectMode" title="批量选择模式">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
选择
</button>
<div id="batchActions" class="batch-actions" style="display: none;">
<button id="selectAll">全选</button>
<button id="batchBookmark">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
收藏
</button>
<button id="batchDelete" class="danger">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
删除
</button>
<span id="selectedCount" class="selected-count">已选 0 条</span>
</div>
<button id="copyFetchLogs" title="复制日志">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
复制
</button>
<button id="exportFetchCSV" title="导出为 CSV">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
CSV
</button>
<button id="showShortcuts" class="icon-btn" title="显示快捷键帮助">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h8M6 16h.01M18 16h.01M10 16h4"/></svg>
</button>
<button id="showSettings" class="icon-btn" title="日志设置">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</button>
<label class="bookmark-toggle" title="只显示收藏的日志">
<input type="checkbox" id="showBookmarksOnly">
<span>⭐</span>
</label>
<label class="switch-toggle">
<span class="switch-label">自动滚动</span>
<input type="checkbox" id="autoScroll" checked>
<span class="switch-slider"></span>
</label>
</div>
<div class="logs-container" id="logsContainer">
<div class="empty-state">
<span class="icon">📋</span>
<p>暂无日志,请先启用调试模式</p>
<button id="enableDebugBtn" class="primary">启用调试</button>
</div>
</div>
</div>
<!-- Console Logs Tab -->
<div class="tab-content" id="consoleTab">
<div class="logs-toolbar">
<select id="filterConsoleLevel">
<option value="">全部级别</option>
<option value="error">Error</option>
<option value="warn">Warn</option>
<option value="info">Info</option>
<option value="log">Log</option>
<option value="debug">Debug</option>
</select>
<input type="text" id="filterConsoleText" placeholder="搜索日志内容...">
<button id="clearConsoleLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
清空
</button>
<button id="copyConsoleLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
复制
</button>
</div>
<div class="logs-container" id="consoleContainer">
<div id="consoleLogsContainer">
<div class="empty-state">
<span class="icon">📝</span>
<p>暂无控制台日志</p>
<p style="font-size: 12px; opacity: 0.7;">warn/error 始终捕获;开启调试后也捕获 log/info/debug(仅在调试模式启用时完整记录)</p>
</div>
</div>
</div>
</div>
<!-- PostMessage Logs Tab -->
<div class="tab-content" id="postmessageTab">
<div class="logs-toolbar">
<button id="togglePmPause" title="暂停/恢复实时日志">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
实时
</button>
<select id="filterMessageSource">
<option value="">全部来源</option>
<option value="main">应用页面</option>
<option value="sw">SW</option>
</select>
<select id="filterMessageTypeSelect">
<option value="">全部消息类型</option>
</select>
<select id="filterPmTimeRange">
<option value="">全部时间</option>
<option value="5">最近 5 分钟</option>
<option value="15">最近 15 分钟</option>
<option value="30">最近 30 分钟</option>
</select>
<input type="text" id="filterMessageType" placeholder="搜索消息类型...">
<button id="clearPostmessageLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
清空
</button>
<button id="copyPostmessageLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
复制
</button>
</div>
<div class="logs-container" id="postmessageContainer">
<div id="postmessageLogsContainer">
<div class="empty-state">
<span class="icon">📨</span>
<p>暂无 PostMessage 日志</p>
<p style="font-size: 12px; opacity: 0.7;">记录主线程与 Service Worker 之间的消息通信(仅在调试模式启用时记录)</p>
</div>
</div>
</div>
</div>
<!-- LLM API Logs Tab -->
<div class="tab-content" id="llmapiTab">
<div class="logs-toolbar">
<select id="filterLLMApiType">
<option value="">全部类型</option>
<option value="image">图片生成</option>
<option value="video">视频生成</option>
<option value="chat">对话</option>
<option value="character">角色</option>
</select>
<select id="filterLLMApiStatus">
<option value="">全部状态</option>
<option value="success">成功</option>
<option value="error">失败</option>
<option value="pending">进行中</option>
</select>
<button id="refreshLLMApiLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
刷新
</button>
<button id="copyLLMApiLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
复制
</button>
<button id="exportLLMApiLogs" class="success">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
导出
</button>
<button id="clearLLMApiLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
清空
</button>
<button id="toggleLLMApiSelectMode">☑️ 选择</button>
<div id="llmapiBatchActions" class="batch-actions" style="display: none;">
<button id="llmapiSelectAll">全选</button>
<button id="llmapiBatchDelete" class="danger">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
删除
</button>
<span id="llmapiSelectedCount" style="color: var(--text-muted); font-size: 12px;">已选 0 条</span>
</div>
</div>
<div class="logs-container" id="llmapiContainer">
<div id="llmapiLogsContainer">
<div class="empty-state">
<span class="icon">🤖</span>
<p>暂无 LLM API 调用记录</p>
<p style="font-size: 12px; opacity: 0.7;">图片/视频/对话等 AI 接口调用会自动记录</p>
</div>
</div>
</div>
</div>
<!-- Gist Management Tab -->
<div class="tab-content" id="gistTab">
<div class="logs-toolbar">
<button id="refreshGistBtn">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
刷新数据
</button>
<span id="gistStatus" style="font-size: 12px; margin-left: 10px; color: var(--text-secondary);">点击刷新加载数据</span>
</div>
<div class="gist-panel-content">
<!-- Section 1: Config Info -->
<div class="gist-section">
<div class="gist-section-header" data-section="config">
<span class="gist-section-icon">⚙️</span>
<span>配置信息</span>
<span class="gist-section-toggle">▼</span>
</div>
<div class="gist-section-content" id="gistConfigSection">
<div class="gist-config-grid">
<div class="gist-config-item">
<span class="label">GitHub Token</span>
<span class="value" id="gistTokenStatus">-</span>
</div>
<div class="gist-config-item">
<span class="label">主 Gist ID</span>
<span class="value" id="gistId">-</span>
</div>
<div class="gist-config-item">
<span class="label">分片系统</span>
<span class="value" id="gistShardEnabled">-</span>
</div>
<div class="gist-config-item">
<span class="label">同步密码</span>
<span class="value" id="gistPasswordStatus">-</span>
</div>
<div class="gist-config-item">
<span class="label">设备 ID</span>
<span class="value" id="gistDeviceId">-</span>
</div>
<div class="gist-config-item">
<span class="label">最后同步</span>
<span class="value" id="gistLastSync">-</span>
</div>
</div>
</div>
</div>
<!-- Section 2: Shard Overview -->
<div class="gist-section">
<div class="gist-section-header" data-section="shards">
<span class="gist-section-icon">📦</span>
<span>分片概览</span>
<span class="gist-section-toggle">▼</span>
</div>
<div class="gist-section-content" id="gistShardSection">
<div class="gist-shard-stats">
<div class="gist-stat-card">
<div class="stat-number" id="shardTotalCount">-</div>
<div class="stat-label">总分片</div>
</div>
<div class="gist-stat-card active">
<div class="stat-number" id="shardActiveCount">-</div>
<div class="stat-label">活跃</div>
</div>
<div class="gist-stat-card full">
<div class="stat-number" id="shardFullCount">-</div>
<div class="stat-label">已满</div>
</div>
<div class="gist-stat-card">
<div class="stat-number" id="shardFileCount">-</div>
<div class="stat-label">总文件</div>
</div>
<div class="gist-stat-card">
<div class="stat-number" id="shardTotalSize">-</div>
<div class="stat-label">总大小</div>
</div>
</div>
<div class="gist-shard-list" id="gistShardList">
<div class="empty-state" style="padding: 20px;">加载分片信息...</div>
</div>
</div>
</div>
<!-- Section 3: Debug Operations -->
<div class="gist-section">
<div class="gist-section-header" data-section="operations">
<span class="gist-section-icon">🛠️</span>
<span>调试操作</span>
<span class="gist-section-toggle">▼</span>
</div>
<div class="gist-section-content" id="gistOperationsSection">
<div class="gist-debug-actions compact">
<!-- Row 1: Connection & Tasks -->
<div class="debug-action-row">
<div class="debug-action-group">
<span class="debug-group-label">连接</span>
<button class="debug-btn" id="debugTestConnection" title="测试 GitHub API 连接">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
测试连接
</button>
<button class="debug-btn" id="debugListGists" title="列出所有同步 Gist">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
列出 Gists
</button>
</div>
<div class="debug-action-group">
<span class="debug-group-label">任务</span>
<button class="debug-btn" id="debugPreviewTaskMerge" title="预览任务合并结果">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
预览合并
</button>
<button class="debug-btn warning" id="debugImportRemoteTasks" title="从远程导入缺失的任务">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
导入任务
</button>
</div>
<div class="debug-action-group">
<span class="debug-group-label">画板</span>
<button class="debug-btn" id="debugPreviewBoardMerge" title="预览画板合并结果">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
预览合并
</button>
<button class="debug-btn" id="debugDownloadBoard" title="下载指定画板内容">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
下载画板
</button>
</div>
</div>
<!-- Row 2: Media & Diagnostics -->
<div class="debug-action-row">
<div class="debug-action-group">
<span class="debug-group-label">媒体</span>
<button class="debug-btn" id="debugListShardFiles" title="列出分片中的所有文件">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
分片列表
</button>
<button class="debug-btn" id="debugTestMediaDownload" title="测试下载单个媒体文件">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
测试下载
</button>
</div>
<div class="debug-action-group">
<span class="debug-group-label">数据库</span>
<button class="debug-btn" id="debugCheckSyncLogDb" title="检查同步日志数据库状态">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
检查日志库
</button>
<button class="debug-btn" id="debugDiagnoseDb" title="诊断所有数据库结构">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
诊断结构
</button>
</div>
<div class="debug-action-group">
<span class="debug-group-label">测试</span>
<button class="debug-btn" id="debugWriteTestLog" title="写入一条测试日志">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
写入日志
</button>
<button class="debug-btn" id="debugPerformanceTest" title="运行性能基准测试">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v4"/><path d="m16.24 7.76-2.12 2.12"/><path d="M21 12h-4"/><path d="m16.24 16.24-2.12-2.12"/><path d="M12 21v-4"/><path d="m7.76 16.24 2.12-2.12"/><path d="M3 12h4"/><path d="m7.76 7.76 2.12 2.12"/></svg>
性能测试
</button>
</div>
</div>
</div>
<p class="debug-hint">调试日志将输出到下方的「同步日志」区域</p>
</div>
</div>
<!-- Section 4: Sync Logs -->
<div class="gist-section gist-section-expanded">
<div class="gist-section-header" data-section="synclogs">
<span class="gist-section-icon">📋</span>
<span>同步日志</span>
<span class="gist-section-toggle">▼</span>
</div>
<div class="gist-section-content" id="gistSyncLogsSection">
<!-- Log Controls -->
<div class="sync-log-controls">
<div class="sync-log-filters">
<select id="syncLogLevelFilter" class="sync-log-select">
<option value="">全部级别</option>
<option value="error">错误</option>
<option value="warning">警告</option>
<option value="success">成功</option>
<option value="info">信息</option>
<option value="debug">调试</option>
</select>
<select id="syncLogCategoryFilter" class="sync-log-select">
<option value="">全部类型</option>
<option value="sync">主同步</option>
<option value="upload">上传</option>
<option value="download">下载</option>
<option value="merge">合并</option>
<option value="conflict">冲突</option>
<option value="media">媒体</option>
<option value="shard">分片</option>
<option value="api">API</option>
</select>
<select id="syncLogSessionFilter" class="sync-log-select">
<option value="">全部会话</option>
</select>
<input type="text" id="syncLogSearch" class="sync-log-search" placeholder="搜索日志..." />
</div>
<div class="sync-log-actions">
<button class="debug-btn" id="refreshSyncLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
刷新
</button>
<button class="debug-btn" id="toggleAutoRefresh" title="自动刷新日志 (3秒间隔)">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<span id="autoRefreshLabel">自动</span>
</button>
<button class="debug-btn" id="copySyncLogs" title="复制当前筛选的日志到剪贴板">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
复制
</button>
<button class="debug-btn" id="exportSyncLogs" title="导出日志为 JSON">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
导出
</button>
<button class="debug-btn warning" id="clearSyncLogs" title="清空所有日志">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
清空
</button>
</div>
</div>
<!-- Log Stats (clickable to filter) -->
<div class="sync-log-stats" id="syncLogStats">
<div class="sync-log-stat clickable" data-filter="" title="显示所有日志">
<span class="stat-value" id="syncLogTotal">-</span>
<span class="stat-label">总条数</span>
</div>
<div class="sync-log-stat error clickable" data-filter="error" title="点击筛选错误日志">
<span class="stat-value" id="syncLogErrors">-</span>
<span class="stat-label">错误</span>
</div>
<div class="sync-log-stat warning clickable" data-filter="warning" title="点击筛选警告日志">
<span class="stat-value" id="syncLogWarnings">-</span>
<span class="stat-label">警告</span>
</div>
<div class="sync-log-stat success clickable" data-filter="success" title="点击筛选成功日志">
<span class="stat-value" id="syncLogSuccesses">-</span>
<span class="stat-label">成功</span>
</div>
<div class="sync-log-stat">
<span class="stat-value" id="syncLogSessions">-</span>
<span class="stat-label">会话数</span>
</div>
</div>
<!-- Log List -->
<div class="sync-log-list" id="syncLogList">
<div class="empty-state" style="padding: 40px;">点击刷新加载同步日志</div>
</div>
<!-- Pagination -->
<div class="sync-log-pagination" id="syncLogPagination">
<button class="debug-btn-small" id="syncLogPrevPage" disabled>上一页</button>
<span id="syncLogPageInfo">-</span>
<button class="debug-btn-small" id="syncLogNextPage" disabled>下一页</button>
</div>
</div>
</div>
<!-- Section 5: Data Diagnostics -->
<div class="gist-section">
<div class="gist-section-header" data-section="diagnostics">
<span class="gist-section-icon">🔍</span>
<span>数据诊断</span>
<span class="gist-section-toggle">▼</span>
</div>
<div class="gist-section-content" id="gistDiagnosticsSection">
<!-- Sub-tabs for diagnostics with refresh button -->
<div class="gist-subtabs" style="display: flex; align-items: center; gap: 12px;">
<div style="display: flex; gap: 4px;">
<button class="gist-subtab active" data-subtab="tasks">任务对比</button>
<button class="gist-subtab" data-subtab="boards">画板对比</button>
<button class="gist-subtab" data-subtab="media">素材对比</button>
<button class="gist-subtab" data-subtab="files">远程文件</button>
</div>
<button class="debug-btn-small" id="refreshLocalDiagnostics" title="刷新本地数据">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:12px;height:12px;"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
刷新本地
</button>
<button class="debug-btn-small" id="loadRemoteDiagnostics" title="从 GitHub Gist 加载远程数据进行对比" style="background: var(--primary-color); color: white; border-color: var(--primary-color);">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:12px;height:12px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
加载远程数据
</button>
</div>
<!-- Tasks Comparison -->
<div class="gist-subtab-content active" id="tasksSubtab">
<div class="gist-comparison-summary" id="taskComparisonSummary">
<div class="comparison-stat">
<span class="label">本地任务</span>
<span class="value" id="localTaskCount">-</span>
</div>
<div class="comparison-stat">
<span class="label">远程任务</span>
<span class="value" id="remoteTaskCount">-</span>
</div>
<div class="comparison-stat local-only">
<span class="label">仅本地</span>
<span class="value" id="localOnlyTaskCount">-</span>
</div>
<div class="comparison-stat remote-only">
<span class="label">仅远程</span>
<span class="value" id="remoteOnlyTaskCount">-</span>
</div>
<div class="comparison-stat synced">
<span class="label">已同步</span>
<span class="value" id="syncedTaskCount">-</span>
</div>
</div>
<div class="gist-comparison-table" id="taskComparisonTable">
<div class="empty-state" style="padding: 20px;">点击刷新加载任务对比数据</div>
</div>
</div>
<!-- Boards Comparison -->
<div class="gist-subtab-content" id="boardsSubtab">
<div class="gist-comparison-summary" id="boardComparisonSummary">
<div class="comparison-stat">
<span class="label">本地画板</span>
<span class="value" id="localBoardCount">-</span>
</div>
<div class="comparison-stat">
<span class="label">远程画板</span>
<span class="value" id="remoteBoardCount">-</span>
</div>
<div class="comparison-stat local-only">
<span class="label">仅本地</span>
<span class="value" id="localOnlyBoardCount">-</span>
</div>
<div class="comparison-stat remote-only">
<span class="label">仅远程</span>
<span class="value" id="remoteOnlyBoardCount">-</span>
</div>
</div>
<div class="gist-comparison-table" id="boardComparisonTable">
<div class="empty-state" style="padding: 20px;">点击刷新加载画板对比数据</div>
</div>
</div>
<!-- Media Comparison -->
<div class="gist-subtab-content" id="mediaSubtab">
<div class="gist-comparison-summary" id="mediaComparisonSummary">
<div class="comparison-stat">
<span class="label">本地缓存</span>
<span class="value" id="localMediaCount">-</span>
</div>
<div class="comparison-stat">
<span class="label">远程索引</span>
<span class="value" id="remoteMediaCount">-</span>
</div>
<div class="comparison-stat local-only">
<span class="label">仅本地</span>
<span class="value" id="localOnlyMediaCount">-</span>
</div>
<div class="comparison-stat remote-only">
<span class="label">仅远程</span>
<span class="value" id="remoteOnlyMediaCount">-</span>
</div>
</div>
<div class="gist-comparison-table" id="mediaComparisonTable">
<div class="empty-state" style="padding: 20px;">点击刷新加载素材对比数据</div>
</div>
</div>
<!-- Remote Files Browser -->
<div class="gist-subtab-content" id="filesSubtab">
<div class="gist-files-container">
<div class="gist-file-list-panel">
<div class="panel-header">文件列表</div>
<div id="gistFileList" class="gist-file-list">
<div class="empty-state" style="padding: 20px;">点击刷新加载文件列表</div>
</div>
</div>
<div class="gist-file-preview-panel">
<div class="panel-header-multi">
<div class="panel-title">内容预览</div>
<div id="previewFileName" class="preview-filename"></div>
</div>
<div id="gistFileContent" class="gist-file-content">
<div class="empty-state" style="padding: 20px;">选择文件查看内容</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Crash Logs Tab -->
<div class="tab-content" id="crashTab">
<div class="logs-toolbar">
<select id="filterCrashType">
<option value="">全部类型</option>
<option value="startup">启动快照</option>
<option value="periodic">定期快照</option>
<option value="error">错误</option>
<option value="beforeunload">页面关闭</option>
<option value="freeze">卡死</option>
<option value="whitescreen">白屏</option>
<option value="longtask">长任务</option>
</select>
<button id="refreshCrashLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
刷新
</button>
<button id="copyCrashLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
复制
</button>
<button id="clearCrashLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
清空
</button>
<button id="exportCrashLogs">
<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
导出
</button>
</div>
<div class="logs-container" id="crashContainer">
<div id="crashLogsContainer">
<div class="empty-state">
<span class="icon">💥</span>
<p>暂无内存日志</p>
<p style="font-size: 12px; opacity: 0.7;">页面启动、内存超限、错误和关闭时的快照会自动记录</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="module" src="./sw-debug/app.js"></script>
</body>
</html>