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.
Files changed (237) hide show
  1. package/.DS_Store +0 -0
  2. package/README.md +47 -0
  3. package/_headers +84 -0
  4. package/_redirects +2 -0
  5. package/assets/ChatMessagesArea-CkUX81uB.js +251 -0
  6. package/assets/ChatMessagesArea-Di0Z80Zh.css +1 -0
  7. package/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  8. package/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  9. package/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  10. package/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  11. package/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  12. package/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  13. package/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  14. package/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  15. package/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  16. package/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  17. package/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  18. package/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  19. package/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  20. package/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  21. package/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  22. package/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  23. package/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  24. package/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  25. package/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  26. package/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  27. package/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  28. package/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  29. package/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  30. package/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  31. package/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  32. package/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  33. package/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  34. package/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  35. package/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  36. package/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  37. package/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  38. package/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  39. package/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  40. package/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  41. package/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  42. package/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  43. package/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  44. package/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  45. package/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  46. package/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  47. package/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  48. package/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  49. package/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  50. package/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  51. package/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  52. package/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  53. package/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  54. package/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  55. package/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  56. package/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  57. package/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  58. package/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  59. package/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  60. package/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  61. package/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  62. package/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  63. package/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  64. package/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  65. package/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  66. package/assets/Tableau10-B-NsZVaP.js +1 -0
  67. package/assets/Tableau10-Dnlau_Wv.js +1 -0
  68. package/assets/ToolboxDrawer-By1XMh8B.js +87 -0
  69. package/assets/ToolboxDrawer-fPqvDLQE.css +1 -0
  70. package/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  71. package/assets/ai-analyze-Db-iXol6.js +1 -0
  72. package/assets/arc-BZXVqUcI.js +1 -0
  73. package/assets/arc-ajYHRRnk.js +1 -0
  74. package/assets/array-B5oSNiGi.js +1 -0
  75. package/assets/array-BKyUJesY.js +1 -0
  76. package/assets/batch-image-generation-Baqb01Lm.js +6 -0
  77. package/assets/batch-image-generation-CbLMWmjk.css +1 -0
  78. package/assets/blockDiagram-38ab4fdb-BT3H_WVv.js +118 -0
  79. package/assets/blockDiagram-38ab4fdb-u0xYP3Lt.js +118 -0
  80. package/assets/c4Diagram-3d4e48cf-CBvM6zjM.js +10 -0
  81. package/assets/c4Diagram-3d4e48cf-WOIEVidH.js +10 -0
  82. package/assets/channel-BP25wTsw.js +1 -0
  83. package/assets/channel-HzrLNFUg.js +1 -0
  84. package/assets/classDiagram-70f12bd4-BMutcvFi.js +2 -0
  85. package/assets/classDiagram-70f12bd4-Cl9U1r5F.js +2 -0
  86. package/assets/classDiagram-v2-f2320105-C0agtbR4.js +2 -0
  87. package/assets/classDiagram-v2-f2320105-tCBzATK6.js +2 -0
  88. package/assets/clone-B69pF7Y_.js +1 -0
  89. package/assets/clone-oX7o-l4R.js +1 -0
  90. package/assets/createText-2e5e7dd3-CZ9_fscE.js +5 -0
  91. package/assets/createText-2e5e7dd3-idrqgJjU.js +7 -0
  92. package/assets/edges-e0da2a9e-C-RyePMV.js +4 -0
  93. package/assets/edges-e0da2a9e-DJXAjJSL.js +4 -0
  94. package/assets/erDiagram-9861fffd-DWJR_3zL.js +51 -0
  95. package/assets/erDiagram-9861fffd-x2Kcy95-.js +51 -0
  96. package/assets/flowDb-956e92f1-BgKjOIdz.js +10 -0
  97. package/assets/flowDb-956e92f1-CF6y18Tn.js +10 -0
  98. package/assets/flowDiagram-66a62f08-BPPw0wPU.js +4 -0
  99. package/assets/flowDiagram-66a62f08-CSAllSFf.js +4 -0
  100. package/assets/flowDiagram-v2-96b9c2cf-B-UGyXRi.js +1 -0
  101. package/assets/flowDiagram-v2-96b9c2cf-Cm596kxZ.js +1 -0
  102. package/assets/flowchart-elk-definition-4a651766-9XSRJbsr.js +139 -0
  103. package/assets/flowchart-elk-definition-4a651766-DWFN9DN3.js +139 -0
  104. package/assets/ganttDiagram-c361ad54-D9tbz9tQ.js +257 -0
  105. package/assets/ganttDiagram-c361ad54-ot5pUYpT.js +257 -0
  106. package/assets/gitGraphDiagram-72cf32ee-BFV3Mt8C.js +70 -0
  107. package/assets/gitGraphDiagram-72cf32ee-C6qFzgGh.js +70 -0
  108. package/assets/graph-BxwlF7JS.js +1 -0
  109. package/assets/graph-D-2Ldvxg.js +1 -0
  110. package/assets/grid-image-cM9AmYC8.js +1 -0
  111. package/assets/has-CgdIPiQG.js +1 -0
  112. package/assets/hasIn-4iY02rGN.js +1 -0
  113. package/assets/index-3862675e-CVZnpwDN.js +1 -0
  114. package/assets/index-3862675e-DqdI9cab.js +1 -0
  115. package/assets/index-B2dvADz8.css +1 -0
  116. package/assets/index-BicRPzXC.js +1 -0
  117. package/assets/index-Bs7-jmv6.css +1 -0
  118. package/assets/index-BwSGXyRr.js +99 -0
  119. package/assets/index-C1XdOOAn.css +1 -0
  120. package/assets/index-C4AKKbpQ.css +1 -0
  121. package/assets/index-CkpXFt8n.js +1 -0
  122. package/assets/index-CrxF9gFe.js +42 -0
  123. package/assets/index-DBWqXBIQ.js +93 -0
  124. package/assets/index-DI_5V2-m.js +3 -0
  125. package/assets/index-DWUAFoZG.js +2064 -0
  126. package/assets/index-Dn0YtZ2R.js +3 -0
  127. package/assets/index-e05Rs4M6.js +12 -0
  128. package/assets/index.dom-C3-224fz.js +1 -0
  129. package/assets/infoDiagram-f8f76790-CnrpwoOt.js +7 -0
  130. package/assets/infoDiagram-f8f76790-FKC1Sy9Y.js +7 -0
  131. package/assets/init-A0kIFD9x.js +1 -0
  132. package/assets/init-Gi6I4Gst.js +1 -0
  133. package/assets/inspiration-board-B_-BBBHt.js +1 -0
  134. package/assets/isEmpty-Dj2GV0v-.js +1 -0
  135. package/assets/journeyDiagram-49397b02-B7fP21sU.js +139 -0
  136. package/assets/journeyDiagram-49397b02-Dp3X9XWq.js +139 -0
  137. package/assets/katex-BbEIqZs1.js +261 -0
  138. package/assets/katex-Cu_Erd72.js +261 -0
  139. package/assets/layout-BD3yCK_X.js +1 -0
  140. package/assets/layout-DHHYqX7p.js +1 -0
  141. package/assets/line-B3bNrkzn.js +1 -0
  142. package/assets/line-B86HLuqu.js +1 -0
  143. package/assets/linear-DU2Ciymb.js +1 -0
  144. package/assets/linear-wCAlMhOS.js +1 -0
  145. package/assets/mermaid.core-DfVvnpgz.js +91 -0
  146. package/assets/mindmap-definition-fc14e90a-D1sxE3xG.js +425 -0
  147. package/assets/mindmap-definition-fc14e90a-YuSOJC7P.js +425 -0
  148. package/assets/ordinal-BRr1uYdk.js +1 -0
  149. package/assets/ordinal-Cboi1Yqb.js +1 -0
  150. package/assets/path-CY0bYimO.js +1 -0
  151. package/assets/path-CbwjOpE9.js +1 -0
  152. package/assets/photo-wall-splitter-BVU2e0aS.js +1 -0
  153. package/assets/pick-Cvlwra4g.js +1 -0
  154. package/assets/pieDiagram-8a3498a8-B6mJUqro.js +35 -0
  155. package/assets/pieDiagram-8a3498a8-B91bWgo_.js +35 -0
  156. package/assets/quadrantDiagram-120e2f19-BxS8fQEz.js +7 -0
  157. package/assets/quadrantDiagram-120e2f19-DwudONqx.js +7 -0
  158. package/assets/requirementDiagram-deff3bca-DygaMIoy.js +52 -0
  159. package/assets/requirementDiagram-deff3bca-v9xlgfS8.js +52 -0
  160. package/assets/sankeyDiagram-04a897e0-BV23dp4l.js +8 -0
  161. package/assets/sankeyDiagram-04a897e0-BXCiXiyw.js +8 -0
  162. package/assets/sequenceDiagram-704730f1-CObRpNi4.js +122 -0
  163. package/assets/sequenceDiagram-704730f1-Ck69A6wI.js +122 -0
  164. package/assets/settings-dialog-BlCO49C4.js +1 -0
  165. package/assets/settings-dialog-QUxXj54T.css +1 -0
  166. package/assets/stateDiagram-587899a1-J_G6I0oo.js +1 -0
  167. package/assets/stateDiagram-587899a1-z-tKclr3.js +1 -0
  168. package/assets/stateDiagram-v2-d93cdb3a-DsThtOzP.js +1 -0
  169. package/assets/stateDiagram-v2-d93cdb3a-XIvq5t8a.js +1 -0
  170. package/assets/styles-6aaf32cf-1fjuNMUk.js +207 -0
  171. package/assets/styles-6aaf32cf-DT2rVNfQ.js +207 -0
  172. package/assets/styles-9a916d00-fLeUSina.js +160 -0
  173. package/assets/styles-9a916d00-q64Umkis.js +160 -0
  174. package/assets/styles-c10674c1-BWlxVc3Q.js +116 -0
  175. package/assets/styles-c10674c1-CtYpjMYU.js +116 -0
  176. package/assets/svgDrawCommon-08f97a94-C_DhKfny.js +1 -0
  177. package/assets/svgDrawCommon-08f97a94-DSBqmUv2.js +1 -0
  178. package/assets/timeline-definition-85554ec2-AKpzwLPN.js +61 -0
  179. package/assets/timeline-definition-85554ec2-dTkYwoLF.js +61 -0
  180. package/assets/ttd-dialog-CxiaIUuJ.js +47 -0
  181. package/assets/ttd-dialog-DCapefb6.css +1 -0
  182. package/assets/upload-4sxUU7q_.js +1 -0
  183. package/assets/video-recovery-service-BckHbSyK.js +1 -0
  184. package/assets/web-vitals-DcvjKPr-.js +1 -0
  185. package/assets/winbox.bundle.min-CoRPjCs5.js +1 -0
  186. package/assets/xlsx-CkFp8p6R.js +105 -0
  187. package/assets/xychartDiagram-e933f94c-DCmvL0ag.js +7 -0
  188. package/assets/xychartDiagram-e933f94c-aqOiXp_u.js +7 -0
  189. package/batch-image.html +1616 -0
  190. package/favicon.ico +0 -0
  191. package/icons/README.md +55 -0
  192. package/icons/aitu10.png +0 -0
  193. package/icons/android-chrome-192x192.png +0 -0
  194. package/icons/android-chrome-512x512.png +0 -0
  195. package/icons/apple-touch-icon.png +0 -0
  196. package/icons/favicon-16x16.png +0 -0
  197. package/icons/favicon-16x16.svg +539 -0
  198. package/icons/favicon-32x32.png +0 -0
  199. package/icons/favicon-32x32.svg +539 -0
  200. package/icons/favicon-new.svg +539 -0
  201. package/icons/favicon-new.svg.png +0 -0
  202. package/icons/icon-192x192.svg +539 -0
  203. package/icons/icon-512x512.svg +539 -0
  204. package/icons/icon-96x96.png +0 -0
  205. package/icons/icon-96x96.svg +539 -0
  206. package/iframe-test.html +340 -0
  207. package/index.html +105 -0
  208. package/init.json +6 -0
  209. package/logo/cardid.jpg +0 -0
  210. package/logo/group-qr.png +0 -0
  211. package/logo/logo_drawnix_h.svg +539 -0
  212. package/logo/logo_drawnix_h_dark.svg +539 -0
  213. package/logo/logo_drawnix_new.svg +539 -0
  214. package/manifest.json +52 -0
  215. package/package.json +31 -0
  216. package/product_showcase/aitu-01.png +0 -0
  217. package/product_showcase/aitu-02.png +0 -0
  218. package/product_showcase/aitu-03.png +0 -0
  219. package/product_showcase/aitu-04.png +0 -0
  220. package/product_showcase/aitu-05.png +0 -0
  221. package/product_showcase/aitu-06.png +0 -0
  222. package/product_showcase/case-1.png +0 -0
  223. package/product_showcase/case-2.png +0 -0
  224. package/robots.txt +13 -0
  225. package/sitemap.xml +29 -0
  226. package/sw-debug/app.js +3069 -0
  227. package/sw-debug/console-entry.js +80 -0
  228. package/sw-debug/log-entry.js +452 -0
  229. package/sw-debug/log-panel.js +309 -0
  230. package/sw-debug/postmessage-entry.js +117 -0
  231. package/sw-debug/status-panel.js +125 -0
  232. package/sw-debug/styles.css +2103 -0
  233. package/sw-debug/sw-communication.js +208 -0
  234. package/sw-debug/utils.js +112 -0
  235. package/sw-debug.html +685 -0
  236. package/sw.js +58 -0
  237. 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
+ }