aitu-app 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.DS_Store +0 -0
- package/README.md +47 -0
- package/_headers +84 -0
- package/_redirects +2 -0
- package/assets/ChatMessagesArea-CkUX81uB.js +251 -0
- package/assets/ChatMessagesArea-Di0Z80Zh.css +1 -0
- package/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/assets/Tableau10-B-NsZVaP.js +1 -0
- package/assets/Tableau10-Dnlau_Wv.js +1 -0
- package/assets/ToolboxDrawer-By1XMh8B.js +87 -0
- package/assets/ToolboxDrawer-fPqvDLQE.css +1 -0
- package/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/assets/ai-analyze-Db-iXol6.js +1 -0
- package/assets/arc-BZXVqUcI.js +1 -0
- package/assets/arc-ajYHRRnk.js +1 -0
- package/assets/array-B5oSNiGi.js +1 -0
- package/assets/array-BKyUJesY.js +1 -0
- package/assets/batch-image-generation-Baqb01Lm.js +6 -0
- package/assets/batch-image-generation-CbLMWmjk.css +1 -0
- package/assets/blockDiagram-38ab4fdb-BT3H_WVv.js +118 -0
- package/assets/blockDiagram-38ab4fdb-u0xYP3Lt.js +118 -0
- package/assets/c4Diagram-3d4e48cf-CBvM6zjM.js +10 -0
- package/assets/c4Diagram-3d4e48cf-WOIEVidH.js +10 -0
- package/assets/channel-BP25wTsw.js +1 -0
- package/assets/channel-HzrLNFUg.js +1 -0
- package/assets/classDiagram-70f12bd4-BMutcvFi.js +2 -0
- package/assets/classDiagram-70f12bd4-Cl9U1r5F.js +2 -0
- package/assets/classDiagram-v2-f2320105-C0agtbR4.js +2 -0
- package/assets/classDiagram-v2-f2320105-tCBzATK6.js +2 -0
- package/assets/clone-B69pF7Y_.js +1 -0
- package/assets/clone-oX7o-l4R.js +1 -0
- package/assets/createText-2e5e7dd3-CZ9_fscE.js +5 -0
- package/assets/createText-2e5e7dd3-idrqgJjU.js +7 -0
- package/assets/edges-e0da2a9e-C-RyePMV.js +4 -0
- package/assets/edges-e0da2a9e-DJXAjJSL.js +4 -0
- package/assets/erDiagram-9861fffd-DWJR_3zL.js +51 -0
- package/assets/erDiagram-9861fffd-x2Kcy95-.js +51 -0
- package/assets/flowDb-956e92f1-BgKjOIdz.js +10 -0
- package/assets/flowDb-956e92f1-CF6y18Tn.js +10 -0
- package/assets/flowDiagram-66a62f08-BPPw0wPU.js +4 -0
- package/assets/flowDiagram-66a62f08-CSAllSFf.js +4 -0
- package/assets/flowDiagram-v2-96b9c2cf-B-UGyXRi.js +1 -0
- package/assets/flowDiagram-v2-96b9c2cf-Cm596kxZ.js +1 -0
- package/assets/flowchart-elk-definition-4a651766-9XSRJbsr.js +139 -0
- package/assets/flowchart-elk-definition-4a651766-DWFN9DN3.js +139 -0
- package/assets/ganttDiagram-c361ad54-D9tbz9tQ.js +257 -0
- package/assets/ganttDiagram-c361ad54-ot5pUYpT.js +257 -0
- package/assets/gitGraphDiagram-72cf32ee-BFV3Mt8C.js +70 -0
- package/assets/gitGraphDiagram-72cf32ee-C6qFzgGh.js +70 -0
- package/assets/graph-BxwlF7JS.js +1 -0
- package/assets/graph-D-2Ldvxg.js +1 -0
- package/assets/grid-image-cM9AmYC8.js +1 -0
- package/assets/has-CgdIPiQG.js +1 -0
- package/assets/hasIn-4iY02rGN.js +1 -0
- package/assets/index-3862675e-CVZnpwDN.js +1 -0
- package/assets/index-3862675e-DqdI9cab.js +1 -0
- package/assets/index-B2dvADz8.css +1 -0
- package/assets/index-BicRPzXC.js +1 -0
- package/assets/index-Bs7-jmv6.css +1 -0
- package/assets/index-BwSGXyRr.js +99 -0
- package/assets/index-C1XdOOAn.css +1 -0
- package/assets/index-C4AKKbpQ.css +1 -0
- package/assets/index-CkpXFt8n.js +1 -0
- package/assets/index-CrxF9gFe.js +42 -0
- package/assets/index-DBWqXBIQ.js +93 -0
- package/assets/index-DI_5V2-m.js +3 -0
- package/assets/index-DWUAFoZG.js +2064 -0
- package/assets/index-Dn0YtZ2R.js +3 -0
- package/assets/index-e05Rs4M6.js +12 -0
- package/assets/index.dom-C3-224fz.js +1 -0
- package/assets/infoDiagram-f8f76790-CnrpwoOt.js +7 -0
- package/assets/infoDiagram-f8f76790-FKC1Sy9Y.js +7 -0
- package/assets/init-A0kIFD9x.js +1 -0
- package/assets/init-Gi6I4Gst.js +1 -0
- package/assets/inspiration-board-B_-BBBHt.js +1 -0
- package/assets/isEmpty-Dj2GV0v-.js +1 -0
- package/assets/journeyDiagram-49397b02-B7fP21sU.js +139 -0
- package/assets/journeyDiagram-49397b02-Dp3X9XWq.js +139 -0
- package/assets/katex-BbEIqZs1.js +261 -0
- package/assets/katex-Cu_Erd72.js +261 -0
- package/assets/layout-BD3yCK_X.js +1 -0
- package/assets/layout-DHHYqX7p.js +1 -0
- package/assets/line-B3bNrkzn.js +1 -0
- package/assets/line-B86HLuqu.js +1 -0
- package/assets/linear-DU2Ciymb.js +1 -0
- package/assets/linear-wCAlMhOS.js +1 -0
- package/assets/mermaid.core-DfVvnpgz.js +91 -0
- package/assets/mindmap-definition-fc14e90a-D1sxE3xG.js +425 -0
- package/assets/mindmap-definition-fc14e90a-YuSOJC7P.js +425 -0
- package/assets/ordinal-BRr1uYdk.js +1 -0
- package/assets/ordinal-Cboi1Yqb.js +1 -0
- package/assets/path-CY0bYimO.js +1 -0
- package/assets/path-CbwjOpE9.js +1 -0
- package/assets/photo-wall-splitter-BVU2e0aS.js +1 -0
- package/assets/pick-Cvlwra4g.js +1 -0
- package/assets/pieDiagram-8a3498a8-B6mJUqro.js +35 -0
- package/assets/pieDiagram-8a3498a8-B91bWgo_.js +35 -0
- package/assets/quadrantDiagram-120e2f19-BxS8fQEz.js +7 -0
- package/assets/quadrantDiagram-120e2f19-DwudONqx.js +7 -0
- package/assets/requirementDiagram-deff3bca-DygaMIoy.js +52 -0
- package/assets/requirementDiagram-deff3bca-v9xlgfS8.js +52 -0
- package/assets/sankeyDiagram-04a897e0-BV23dp4l.js +8 -0
- package/assets/sankeyDiagram-04a897e0-BXCiXiyw.js +8 -0
- package/assets/sequenceDiagram-704730f1-CObRpNi4.js +122 -0
- package/assets/sequenceDiagram-704730f1-Ck69A6wI.js +122 -0
- package/assets/settings-dialog-BlCO49C4.js +1 -0
- package/assets/settings-dialog-QUxXj54T.css +1 -0
- package/assets/stateDiagram-587899a1-J_G6I0oo.js +1 -0
- package/assets/stateDiagram-587899a1-z-tKclr3.js +1 -0
- package/assets/stateDiagram-v2-d93cdb3a-DsThtOzP.js +1 -0
- package/assets/stateDiagram-v2-d93cdb3a-XIvq5t8a.js +1 -0
- package/assets/styles-6aaf32cf-1fjuNMUk.js +207 -0
- package/assets/styles-6aaf32cf-DT2rVNfQ.js +207 -0
- package/assets/styles-9a916d00-fLeUSina.js +160 -0
- package/assets/styles-9a916d00-q64Umkis.js +160 -0
- package/assets/styles-c10674c1-BWlxVc3Q.js +116 -0
- package/assets/styles-c10674c1-CtYpjMYU.js +116 -0
- package/assets/svgDrawCommon-08f97a94-C_DhKfny.js +1 -0
- package/assets/svgDrawCommon-08f97a94-DSBqmUv2.js +1 -0
- package/assets/timeline-definition-85554ec2-AKpzwLPN.js +61 -0
- package/assets/timeline-definition-85554ec2-dTkYwoLF.js +61 -0
- package/assets/ttd-dialog-CxiaIUuJ.js +47 -0
- package/assets/ttd-dialog-DCapefb6.css +1 -0
- package/assets/upload-4sxUU7q_.js +1 -0
- package/assets/video-recovery-service-BckHbSyK.js +1 -0
- package/assets/web-vitals-DcvjKPr-.js +1 -0
- package/assets/winbox.bundle.min-CoRPjCs5.js +1 -0
- package/assets/xlsx-CkFp8p6R.js +105 -0
- package/assets/xychartDiagram-e933f94c-DCmvL0ag.js +7 -0
- package/assets/xychartDiagram-e933f94c-aqOiXp_u.js +7 -0
- package/batch-image.html +1616 -0
- package/favicon.ico +0 -0
- package/icons/README.md +55 -0
- package/icons/aitu10.png +0 -0
- package/icons/android-chrome-192x192.png +0 -0
- package/icons/android-chrome-512x512.png +0 -0
- package/icons/apple-touch-icon.png +0 -0
- package/icons/favicon-16x16.png +0 -0
- package/icons/favicon-16x16.svg +539 -0
- package/icons/favicon-32x32.png +0 -0
- package/icons/favicon-32x32.svg +539 -0
- package/icons/favicon-new.svg +539 -0
- package/icons/favicon-new.svg.png +0 -0
- package/icons/icon-192x192.svg +539 -0
- package/icons/icon-512x512.svg +539 -0
- package/icons/icon-96x96.png +0 -0
- package/icons/icon-96x96.svg +539 -0
- package/iframe-test.html +340 -0
- package/index.html +105 -0
- package/init.json +6 -0
- package/logo/cardid.jpg +0 -0
- package/logo/group-qr.png +0 -0
- package/logo/logo_drawnix_h.svg +539 -0
- package/logo/logo_drawnix_h_dark.svg +539 -0
- package/logo/logo_drawnix_new.svg +539 -0
- package/manifest.json +52 -0
- package/package.json +31 -0
- package/product_showcase/aitu-01.png +0 -0
- package/product_showcase/aitu-02.png +0 -0
- package/product_showcase/aitu-03.png +0 -0
- package/product_showcase/aitu-04.png +0 -0
- package/product_showcase/aitu-05.png +0 -0
- package/product_showcase/aitu-06.png +0 -0
- package/product_showcase/case-1.png +0 -0
- package/product_showcase/case-2.png +0 -0
- package/robots.txt +13 -0
- package/sitemap.xml +29 -0
- package/sw-debug/app.js +3069 -0
- package/sw-debug/console-entry.js +80 -0
- package/sw-debug/log-entry.js +452 -0
- package/sw-debug/log-panel.js +309 -0
- package/sw-debug/postmessage-entry.js +117 -0
- package/sw-debug/status-panel.js +125 -0
- package/sw-debug/styles.css +2103 -0
- package/sw-debug/sw-communication.js +208 -0
- package/sw-debug/utils.js +112 -0
- package/sw-debug.html +685 -0
- package/sw.js +58 -0
- package/version.json +10 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SW Debug Panel - Unified Log Panel Component
|
|
3
|
+
* 统一的日志面板组件,用于抽象 Fetch/Console/PostMessage/LLM API/内存日志的公共逻辑
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { formatTime, escapeHtml } from './utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 日志面板配置
|
|
10
|
+
* @typedef {Object} LogPanelConfig
|
|
11
|
+
* @property {string} id - 面板唯一标识
|
|
12
|
+
* @property {string} name - 面板名称
|
|
13
|
+
* @property {string} emptyIcon - 空状态图标
|
|
14
|
+
* @property {string} emptyText - 空状态主文本
|
|
15
|
+
* @property {string} [emptySubtext] - 空状态副文本
|
|
16
|
+
* @property {Function} renderEntry - 渲染单个日志条目的函数
|
|
17
|
+
* @property {Function} formatForCopy - 格式化日志用于复制的函数
|
|
18
|
+
* @property {Array<FilterConfig>} filters - 过滤器配置
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 过滤器配置
|
|
23
|
+
* @typedef {Object} FilterConfig
|
|
24
|
+
* @property {string} type - 过滤器类型: 'select' | 'input'
|
|
25
|
+
* @property {string} id - 过滤器 DOM ID
|
|
26
|
+
* @property {string} [placeholder] - 输入框占位符
|
|
27
|
+
* @property {Array<{value: string, label: string}>} [options] - 下拉选项
|
|
28
|
+
* @property {Function} filter - 过滤函数 (log, filterValue) => boolean
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 创建统一的日志面板
|
|
33
|
+
*/
|
|
34
|
+
export class LogPanel {
|
|
35
|
+
/**
|
|
36
|
+
* @param {LogPanelConfig} config
|
|
37
|
+
*/
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.config = config;
|
|
40
|
+
this.logs = [];
|
|
41
|
+
this.expandedIds = new Set();
|
|
42
|
+
this.filterValues = {};
|
|
43
|
+
this.elements = null;
|
|
44
|
+
this.maxLogs = 500;
|
|
45
|
+
this.maxDisplay = 200;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 初始化面板,绑定 DOM 元素
|
|
50
|
+
* @param {Object} elements - DOM 元素映射
|
|
51
|
+
*/
|
|
52
|
+
init(elements) {
|
|
53
|
+
this.elements = elements;
|
|
54
|
+
this.setupFilterListeners();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 设置过滤器事件监听
|
|
59
|
+
*/
|
|
60
|
+
setupFilterListeners() {
|
|
61
|
+
this.config.filters.forEach(filter => {
|
|
62
|
+
const el = this.elements[filter.id];
|
|
63
|
+
if (!el) return;
|
|
64
|
+
|
|
65
|
+
if (filter.type === 'select') {
|
|
66
|
+
el.addEventListener('change', () => {
|
|
67
|
+
this.filterValues[filter.id] = el.value;
|
|
68
|
+
this.render();
|
|
69
|
+
});
|
|
70
|
+
} else if (filter.type === 'input') {
|
|
71
|
+
el.addEventListener('input', () => {
|
|
72
|
+
this.filterValues[filter.id] = el.value;
|
|
73
|
+
this.render();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 添加日志条目
|
|
81
|
+
* @param {Object} log
|
|
82
|
+
*/
|
|
83
|
+
addLog(log) {
|
|
84
|
+
// 检查重复
|
|
85
|
+
if (this.logs.some(l => l.id === log.id)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.logs.unshift(log);
|
|
90
|
+
if (this.logs.length > this.maxLogs) {
|
|
91
|
+
this.logs.pop();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 更新日志条目
|
|
97
|
+
* @param {Object} log
|
|
98
|
+
*/
|
|
99
|
+
updateLog(log) {
|
|
100
|
+
const index = this.logs.findIndex(l => l.id === log.id);
|
|
101
|
+
if (index >= 0) {
|
|
102
|
+
this.logs[index] = { ...this.logs[index], ...log };
|
|
103
|
+
} else {
|
|
104
|
+
this.addLog(log);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 设置所有日志
|
|
110
|
+
* @param {Array} logs
|
|
111
|
+
*/
|
|
112
|
+
setLogs(logs) {
|
|
113
|
+
this.logs = logs || [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 清空日志
|
|
118
|
+
*/
|
|
119
|
+
clearLogs() {
|
|
120
|
+
this.logs = [];
|
|
121
|
+
this.expandedIds.clear();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 获取过滤后的日志
|
|
126
|
+
* @returns {Array}
|
|
127
|
+
*/
|
|
128
|
+
getFilteredLogs() {
|
|
129
|
+
let filtered = this.logs;
|
|
130
|
+
|
|
131
|
+
this.config.filters.forEach(filter => {
|
|
132
|
+
const value = this.filterValues[filter.id] || '';
|
|
133
|
+
if (value && filter.filter) {
|
|
134
|
+
filtered = filtered.filter(log => filter.filter(log, value));
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return filtered;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 渲染日志列表
|
|
143
|
+
*/
|
|
144
|
+
render() {
|
|
145
|
+
const container = this.elements.container;
|
|
146
|
+
if (!container) return;
|
|
147
|
+
|
|
148
|
+
const filteredLogs = this.getFilteredLogs();
|
|
149
|
+
|
|
150
|
+
if (filteredLogs.length === 0) {
|
|
151
|
+
container.innerHTML = this.renderEmptyState();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
container.innerHTML = '';
|
|
156
|
+
filteredLogs.slice(0, this.maxDisplay).forEach(log => {
|
|
157
|
+
const isExpanded = this.expandedIds.has(log.id);
|
|
158
|
+
const entry = this.config.renderEntry(log, isExpanded, (id, expanded) => {
|
|
159
|
+
if (expanded) {
|
|
160
|
+
this.expandedIds.add(id);
|
|
161
|
+
} else {
|
|
162
|
+
this.expandedIds.delete(id);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
container.appendChild(entry);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 渲染空状态
|
|
171
|
+
* @returns {string}
|
|
172
|
+
*/
|
|
173
|
+
renderEmptyState() {
|
|
174
|
+
return `
|
|
175
|
+
<div class="empty-state">
|
|
176
|
+
<span class="icon">${this.config.emptyIcon}</span>
|
|
177
|
+
<p>${this.config.emptyText}</p>
|
|
178
|
+
${this.config.emptySubtext ? `<p style="font-size: 12px; opacity: 0.7;">${this.config.emptySubtext}</p>` : ''}
|
|
179
|
+
</div>
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 复制过滤后的日志到剪贴板
|
|
185
|
+
* @returns {Promise<{success: boolean, count: number}>}
|
|
186
|
+
*/
|
|
187
|
+
async copyLogs() {
|
|
188
|
+
const filteredLogs = this.getFilteredLogs();
|
|
189
|
+
|
|
190
|
+
if (filteredLogs.length === 0) {
|
|
191
|
+
return { success: false, count: 0 };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const text = filteredLogs.map(log => this.config.formatForCopy(log)).join('\n\n');
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
await navigator.clipboard.writeText(text);
|
|
198
|
+
return { success: true, count: filteredLogs.length };
|
|
199
|
+
} catch (err) {
|
|
200
|
+
console.error('Failed to copy:', err);
|
|
201
|
+
return { success: false, count: 0 };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 获取日志统计信息
|
|
207
|
+
* @returns {Object}
|
|
208
|
+
*/
|
|
209
|
+
getStats() {
|
|
210
|
+
return {
|
|
211
|
+
total: this.logs.length,
|
|
212
|
+
filtered: this.getFilteredLogs().length,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 创建复制按钮的点击处理器
|
|
219
|
+
* @param {LogPanel} panel - 日志面板实例
|
|
220
|
+
* @param {HTMLElement} button - 按钮元素
|
|
221
|
+
* @returns {Function}
|
|
222
|
+
*/
|
|
223
|
+
export function createCopyHandler(panel, button) {
|
|
224
|
+
return async () => {
|
|
225
|
+
const result = await panel.copyLogs();
|
|
226
|
+
if (result.success) {
|
|
227
|
+
const originalText = button.textContent;
|
|
228
|
+
button.textContent = `✅ 已复制 (${result.count})`;
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
button.textContent = originalText;
|
|
231
|
+
}, 1500);
|
|
232
|
+
} else if (result.count === 0) {
|
|
233
|
+
alert('没有可复制的日志');
|
|
234
|
+
} else {
|
|
235
|
+
alert('复制失败');
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 工具栏按钮组件
|
|
242
|
+
*/
|
|
243
|
+
export class ToolbarButton {
|
|
244
|
+
/**
|
|
245
|
+
* 创建工具栏按钮
|
|
246
|
+
* @param {Object} options
|
|
247
|
+
* @param {string} options.icon - 按钮图标
|
|
248
|
+
* @param {string} options.text - 按钮文本
|
|
249
|
+
* @param {string} [options.className] - 额外的 CSS 类
|
|
250
|
+
* @param {Function} options.onClick - 点击处理函数
|
|
251
|
+
* @returns {HTMLElement}
|
|
252
|
+
*/
|
|
253
|
+
static create({ icon, text, className = '', onClick }) {
|
|
254
|
+
const button = document.createElement('button');
|
|
255
|
+
button.style.cssText = 'padding: 6px 12px; font-size: 12px;';
|
|
256
|
+
if (className) {
|
|
257
|
+
button.className = className;
|
|
258
|
+
}
|
|
259
|
+
button.innerHTML = `${icon ? icon + ' ' : ''}${text}`;
|
|
260
|
+
button.addEventListener('click', onClick);
|
|
261
|
+
return button;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 从日志数组中提取唯一的消息类型
|
|
267
|
+
* @param {Array} logs - 日志数组
|
|
268
|
+
* @param {string} typeField - 类型字段名
|
|
269
|
+
* @returns {Array<string>}
|
|
270
|
+
*/
|
|
271
|
+
export function extractUniqueTypes(logs, typeField = 'messageType') {
|
|
272
|
+
const types = new Set();
|
|
273
|
+
logs.forEach(log => {
|
|
274
|
+
const type = log[typeField];
|
|
275
|
+
if (type) {
|
|
276
|
+
types.add(type);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
return Array.from(types).sort();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 更新消息类型下拉选项
|
|
284
|
+
* @param {HTMLSelectElement} select - 下拉选择器
|
|
285
|
+
* @param {Array<string>} types - 类型列表
|
|
286
|
+
* @param {string} currentValue - 当前选中值
|
|
287
|
+
*/
|
|
288
|
+
export function updateTypeSelectOptions(select, types, currentValue = '') {
|
|
289
|
+
// 保存当前值
|
|
290
|
+
const selectedValue = currentValue || select.value;
|
|
291
|
+
|
|
292
|
+
// 清空现有选项(保留第一个"全部"选项)
|
|
293
|
+
while (select.options.length > 1) {
|
|
294
|
+
select.remove(1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 添加新选项
|
|
298
|
+
types.forEach(type => {
|
|
299
|
+
const option = document.createElement('option');
|
|
300
|
+
option.value = type;
|
|
301
|
+
option.textContent = type;
|
|
302
|
+
select.appendChild(option);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// 恢复选中值
|
|
306
|
+
if (selectedValue && types.includes(selectedValue)) {
|
|
307
|
+
select.value = selectedValue;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SW Debug Panel - PostMessage Entry Component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { formatTime, escapeHtml } from './utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Format JSON data for display
|
|
9
|
+
* @param {any} data
|
|
10
|
+
* @param {number} maxLength - Max string length before truncation
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
function formatData(data, maxLength = 500) {
|
|
14
|
+
if (data === undefined) return '<span class="pm-undefined">undefined</span>';
|
|
15
|
+
if (data === null) return '<span class="pm-null">null</span>';
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const str = JSON.stringify(data, null, 2);
|
|
19
|
+
if (str.length > maxLength) {
|
|
20
|
+
return escapeHtml(str.slice(0, maxLength)) + '...';
|
|
21
|
+
}
|
|
22
|
+
return escapeHtml(str);
|
|
23
|
+
} catch {
|
|
24
|
+
return escapeHtml(String(data));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get a short preview of the message data
|
|
30
|
+
* @param {any} data
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function getDataPreview(data) {
|
|
34
|
+
if (data === undefined || data === null) return '';
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const str = JSON.stringify(data);
|
|
38
|
+
if (str.length > 60) {
|
|
39
|
+
return escapeHtml(str.slice(0, 60)) + '...';
|
|
40
|
+
}
|
|
41
|
+
return escapeHtml(str);
|
|
42
|
+
} catch {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a PostMessage log entry DOM element
|
|
49
|
+
* @param {object} log - The log entry object
|
|
50
|
+
* @param {boolean} isExpanded - Initial expanded state
|
|
51
|
+
* @param {Function} onToggle - Callback when expand state changes (id, expanded)
|
|
52
|
+
* @returns {HTMLElement}
|
|
53
|
+
*/
|
|
54
|
+
export function createPostMessageEntry(log, isExpanded = false, onToggle = null) {
|
|
55
|
+
const entry = document.createElement('div');
|
|
56
|
+
const directionClass = log.direction === 'send' ? 'send' : 'receive';
|
|
57
|
+
// 使用 log-entry 作为基础类,与 Fetch 日志统一
|
|
58
|
+
entry.className = `log-entry pm-entry${isExpanded ? ' expanded' : ''}`;
|
|
59
|
+
entry.dataset.id = log.id;
|
|
60
|
+
|
|
61
|
+
const directionIcon = log.direction === 'send' ? '→' : '←';
|
|
62
|
+
const directionLabel = log.direction === 'send' ? '发送' : '接收';
|
|
63
|
+
const directionTarget = log.direction === 'send' ? 'SW' : '主线程';
|
|
64
|
+
|
|
65
|
+
const messageType = log.messageType || 'unknown';
|
|
66
|
+
const dataPreview = getDataPreview(log.data);
|
|
67
|
+
|
|
68
|
+
entry.innerHTML = `
|
|
69
|
+
<div class="log-header pm-header">
|
|
70
|
+
<span class="log-toggle"><span class="arrow">▶</span></span>
|
|
71
|
+
<span class="log-time pm-time">${formatTime(log.timestamp)}</span>
|
|
72
|
+
<span class="pm-direction ${directionClass}">
|
|
73
|
+
<span class="pm-direction-icon">${directionIcon}</span>${directionLabel}
|
|
74
|
+
</span>
|
|
75
|
+
<span class="pm-type">${escapeHtml(messageType)}</span>
|
|
76
|
+
<span class="log-url pm-preview">${dataPreview}</span>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="log-details pm-details">
|
|
79
|
+
<div class="detail-section">
|
|
80
|
+
<h4>基本信息</h4>
|
|
81
|
+
<pre>方向: ${directionLabel} ${directionTarget}
|
|
82
|
+
消息类型: ${messageType}
|
|
83
|
+
时间: ${new Date(log.timestamp).toLocaleString('zh-CN')}</pre>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="detail-section">
|
|
86
|
+
<h4>消息数据</h4>
|
|
87
|
+
<pre>${formatData(log.data)}</pre>
|
|
88
|
+
</div>
|
|
89
|
+
${log.response !== undefined ? `
|
|
90
|
+
<div class="detail-section">
|
|
91
|
+
<h4>响应数据</h4>
|
|
92
|
+
<pre style="border-left: 3px solid var(--success-color);">${formatData(log.response)}</pre>
|
|
93
|
+
</div>
|
|
94
|
+
` : ''}
|
|
95
|
+
${log.error ? `
|
|
96
|
+
<div class="detail-section">
|
|
97
|
+
<h4>错误信息</h4>
|
|
98
|
+
<pre style="color: var(--error-color);">${escapeHtml(log.error)}</pre>
|
|
99
|
+
</div>
|
|
100
|
+
` : ''}
|
|
101
|
+
</div>
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
// Toggle expand on toggle button click
|
|
105
|
+
const toggleBtn = entry.querySelector('.log-toggle');
|
|
106
|
+
if (toggleBtn) {
|
|
107
|
+
toggleBtn.addEventListener('click', (e) => {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
const isNowExpanded = entry.classList.toggle('expanded');
|
|
110
|
+
if (onToggle) {
|
|
111
|
+
onToggle(log.id, isNowExpanded);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return entry;
|
|
117
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SW Debug Panel - Status Panel Component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { formatSize } from './utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Update SW status indicator
|
|
9
|
+
* @param {HTMLElement} element
|
|
10
|
+
* @param {boolean} connected
|
|
11
|
+
* @param {string} version
|
|
12
|
+
*/
|
|
13
|
+
export function updateSwStatus(element, connected, version) {
|
|
14
|
+
if (connected) {
|
|
15
|
+
element.className = 'status-indicator active';
|
|
16
|
+
element.querySelector('.text').textContent = version ? `v${version}` : '已连接';
|
|
17
|
+
} else {
|
|
18
|
+
element.className = 'status-indicator inactive';
|
|
19
|
+
element.querySelector('.text').textContent = '未连接';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Update status panel with SW status data
|
|
25
|
+
* @param {object} status
|
|
26
|
+
* @param {object} elements - DOM elements
|
|
27
|
+
*/
|
|
28
|
+
export function updateStatusPanel(status, elements) {
|
|
29
|
+
elements.swVersion.textContent = status.version || '-';
|
|
30
|
+
elements.debugMode.textContent = status.debugModeEnabled ? '开启' : '关闭';
|
|
31
|
+
elements.pendingImages.textContent = status.pendingImageRequests || 0;
|
|
32
|
+
elements.pendingVideos.textContent = status.pendingVideoRequests || 0;
|
|
33
|
+
|
|
34
|
+
// Video blob cache with size info
|
|
35
|
+
const videoCacheCount = status.videoBlobCacheSize || 0;
|
|
36
|
+
const videoCacheBytes = status.videoBlobCacheTotalBytes || 0;
|
|
37
|
+
if (videoCacheBytes > 0) {
|
|
38
|
+
elements.videoBlobCache.textContent = `${videoCacheCount} (${formatSize(videoCacheBytes)})`;
|
|
39
|
+
} else {
|
|
40
|
+
elements.videoBlobCache.textContent = videoCacheCount;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
elements.completedRequests.textContent = status.completedImageRequestsSize || 0;
|
|
44
|
+
elements.workflowHandler.textContent = status.workflowHandlerInitialized ? '已初始化' : '未初始化';
|
|
45
|
+
elements.debugLogsCount.textContent = status.debugLogsCount || 0;
|
|
46
|
+
|
|
47
|
+
// Failed domains
|
|
48
|
+
if (status.failedDomains && status.failedDomains.length > 0) {
|
|
49
|
+
elements.failedDomainsSection.style.display = 'block';
|
|
50
|
+
elements.failedDomains.innerHTML = status.failedDomains
|
|
51
|
+
.map(d => `<span class="failed-domain-tag">${d}</span>`)
|
|
52
|
+
.join('');
|
|
53
|
+
} else {
|
|
54
|
+
elements.failedDomainsSection.style.display = 'none';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Cache stats
|
|
58
|
+
if (status.cacheStats) {
|
|
59
|
+
updateCacheList(elements.cacheList, status.cacheStats);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return status.debugModeEnabled;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Update cache list with stats
|
|
67
|
+
* @param {HTMLElement} cacheList
|
|
68
|
+
* @param {object} cacheStats
|
|
69
|
+
*/
|
|
70
|
+
export function updateCacheList(cacheList, cacheStats) {
|
|
71
|
+
const entries = Object.entries(cacheStats);
|
|
72
|
+
|
|
73
|
+
// 分离 Cache API 和 IndexedDB
|
|
74
|
+
const cacheEntries = entries.filter(([, s]) => s.type !== 'indexeddb');
|
|
75
|
+
const idbEntries = entries.filter(([, s]) => s.type === 'indexeddb');
|
|
76
|
+
|
|
77
|
+
let html = '';
|
|
78
|
+
|
|
79
|
+
// Cache API 部分
|
|
80
|
+
if (cacheEntries.length > 0) {
|
|
81
|
+
html += '<li class="cache-section-header">Cache API</li>';
|
|
82
|
+
html += cacheEntries.map(([name, stats]) => `
|
|
83
|
+
<li class="cache-item">
|
|
84
|
+
<span class="name">${name.replace('drawnix-', '')}</span>
|
|
85
|
+
<span class="stats">
|
|
86
|
+
<span>${stats.count} 项</span>
|
|
87
|
+
<span>${formatSize(stats.totalSize)}</span>
|
|
88
|
+
</span>
|
|
89
|
+
</li>
|
|
90
|
+
`).join('');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// IndexedDB 部分
|
|
94
|
+
if (idbEntries.length > 0) {
|
|
95
|
+
html += '<li class="cache-section-header">IndexedDB</li>';
|
|
96
|
+
html += idbEntries.map(([name, stats]) => `
|
|
97
|
+
<li class="cache-item">
|
|
98
|
+
<span class="name">${name.replace('[IDB] ', '').replace('drawnix-', '')}</span>
|
|
99
|
+
<span class="stats">
|
|
100
|
+
<span>${stats.count} 条</span>
|
|
101
|
+
<span>~${formatSize(stats.totalSize)}</span>
|
|
102
|
+
</span>
|
|
103
|
+
</li>
|
|
104
|
+
`).join('');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
cacheList.innerHTML = html || '<li class="cache-item"><span class="name">暂无缓存</span></li>';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update debug button state
|
|
112
|
+
* @param {HTMLElement} button
|
|
113
|
+
* @param {boolean} enabled
|
|
114
|
+
*/
|
|
115
|
+
export function updateDebugButton(button, enabled) {
|
|
116
|
+
if (enabled) {
|
|
117
|
+
button.innerHTML = '<span>⏸</span> 停止调试';
|
|
118
|
+
button.classList.remove('primary');
|
|
119
|
+
button.classList.add('danger');
|
|
120
|
+
} else {
|
|
121
|
+
button.innerHTML = '<span>▶</span> 启用调试';
|
|
122
|
+
button.classList.remove('danger');
|
|
123
|
+
button.classList.add('primary');
|
|
124
|
+
}
|
|
125
|
+
}
|