Files
aitu/apps/web/public/sw-debug.html
T
2026-01-20 09:07:22 +08:00

686 lines
35 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">
</head>
<body>
<header>
<h1>
<span class="icon">⚙️</span>
Service Worker 调试面板
<span id="swStatus" class="status-indicator inactive">
<span class="dot"></span>
<span class="text">未连接</span>
</span>
</h1>
<div class="header-actions">
<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">
<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="send">
<span>发送 → SW</span>
</label>
<label class="export-checkbox">
<input type="checkbox" name="postmessage" value="receive">
<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">
<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">
<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" 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">
<select id="filterMessageDirection">
<option value="">全部方向</option>
<option value="send">发送 → SW</option>
<option value="receive">接收 ← 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>
</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>
<!-- 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>