@agenthippo-ai/agenthippo 1.110.9 → 1.110.19

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 (533) hide show
  1. package/out/agentpack/types.js +2 -5
  2. package/out/analytics/dashboard/artifactManager.js +2 -439
  3. package/out/analytics/dashboard/fleetDisplayUtils.js +2 -162
  4. package/out/analytics/dashboard/fleetOrchestrator.js +3 -142
  5. package/out/analytics/dashboard/fleetRegistry.js +2 -79
  6. package/out/analytics/dashboard/fleetRowDefaults.js +2 -87
  7. package/out/analytics/dashboard/fleetScriptAdapter.js +2 -146
  8. package/out/analytics/dashboard/fleetTypes.js +2 -29
  9. package/out/analytics/dashboard/index.js +2 -17
  10. package/out/analytics/dashboard/indexingService.js +2 -422
  11. package/out/analytics/dashboard/localFleetAdapter.js +2 -222
  12. package/out/analytics/dashboard/performanceMonitor.js +5 -76
  13. package/out/analytics/dashboard/processManager.js +2 -209
  14. package/out/analytics/dashboard/provider.js +38 -3817
  15. package/out/analytics/dashboard/queryRunner.js +62 -750
  16. package/out/analytics/dashboard/scheduledFleetStorage.js +2 -241
  17. package/out/analytics/dashboard/sessionJsonlFleet.js +3 -204
  18. package/out/analytics/dashboard/teamViewDiscovery.js +2 -106
  19. package/out/analytics/dashboard/webview/panel.js +34 -2318
  20. package/out/analytics/dashboard/webview/runtime-bridge.js +2 -82
  21. package/out/analytics/lifecycle.js +2 -43
  22. package/out/analytics/service.js +8 -1277
  23. package/out/auth/authBaseUrl.js +2 -59
  24. package/out/auth/bootstrapApply.js +2 -71
  25. package/out/auth/brokerClient.js +2 -160
  26. package/out/auth/callbackUri.js +2 -59
  27. package/out/auth/githubStoreGate.js +2 -70
  28. package/out/auth/loopbackAuthServer.js +4 -159
  29. package/out/auth/onboardingPrompt.js +2 -112
  30. package/out/auth/pkce.js +2 -9
  31. package/out/auth/provider.js +2 -226
  32. package/out/auth/sessionStore.js +2 -156
  33. package/out/auth/signInFlow.js +2 -183
  34. package/out/cli/index.js +619 -551
  35. package/out/cli/run-custom-engine-runner.js +3 -318
  36. package/out/cli/vscode-shim.js +2 -86
  37. package/out/engines/factory.js +2 -45
  38. package/out/engines/types.js +2 -17
  39. package/out/examples/run-headless-repl.js +6 -286
  40. package/out/examples/run-headless.js +2 -187
  41. package/out/extension.js +32 -4473
  42. package/out/gateway/gatewayRouteIndex.js +6 -371
  43. package/out/gateway/gatewaySessionStore.js +3 -243
  44. package/out/gateway/index.js +2 -31
  45. package/out/gateway/resetMappings.js +2 -52
  46. package/out/gateway/service.js +5 -1249
  47. package/out/mcp/publish.js +2 -143
  48. package/out/remote/handleRemoteChat.js +2 -169
  49. package/out/remote/remotePackStatusBar.js +3 -103
  50. package/out/remote/remoteTrust.js +6 -277
  51. package/out/remote/serveClient.js +4 -277
  52. package/out/remoteSkills.js +3 -191
  53. package/out/runtime/index.js +2 -32
  54. package/out/runtime/vscode/index.js +2 -20
  55. package/out/runtime/vscode/vscode-config-provider.js +2 -77
  56. package/out/runtime/vscode/vscode-edit-tracker.js +2 -122
  57. package/out/runtime/vscode/vscode-logger.js +2 -30
  58. package/out/runtime/vscode/vscode-permission-handler.js +2 -72
  59. package/out/scripts/agentide-litellm.sh +0 -1
  60. package/out/scripts/agentide_litellm_hooks_v1.py +170 -418
  61. package/out/scripts/analytics/agent-fleet-widget.js +18 -257
  62. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +4 -4
  63. package/out/scripts/analytics/duckdbLocator.js +2 -28
  64. package/out/scripts/analytics/generate-default-charts.py +43 -323
  65. package/out/scripts/analytics/hotspot-analysis-widget.js +4 -430
  66. package/out/scripts/analytics/index-traces.py +78 -1036
  67. package/out/scripts/analytics/session-comparison-widget.js +26 -472
  68. package/out/scripts/analytics/session-timeline-widget.js +25 -846
  69. package/out/scripts/analytics/setup-analytics.ps1 +8 -1
  70. package/out/scripts/analytics/setup-analytics.sh +13 -2
  71. package/out/scripts/analytics/token-cost-flamegraph-widget.js +4 -461
  72. package/out/scripts/analytics/token-cost-trend-widget.js +4 -534
  73. package/out/scripts/analytics/workflows-collector.cjs +4 -314
  74. package/out/scripts/analytics/workflows-daemon.mjs +9 -185
  75. package/out/scripts/analytics/workflows-widget.cjs +15 -159
  76. package/out/scripts/analytics/wow-model-widget.js +10 -120
  77. package/out/scripts/vendor-tools.mjs +2 -233
  78. package/out/skills/publish.js +3 -178
  79. package/out/store/github/setup.js +7 -212
  80. package/out/store/oramaIndex.js +2 -884
  81. package/out/sync.js +26 -1829
  82. package/out/terminalLogger.js +5 -358
  83. package/out/utils/pathSafety.js +2 -60
  84. package/out/utils/pythonRunner.js +4 -258
  85. package/out/utils/toolPath.js +2 -83
  86. package/out/workspaceFolders.js +2 -59
  87. package/package.json +1 -1
  88. package/out/agentpack/github.js +0 -547
  89. package/out/agentpack/index.js +0 -67
  90. package/out/agentpack/install.js +0 -297
  91. package/out/agentpack/manager.js +0 -441
  92. package/out/agentpack/manifest.js +0 -135
  93. package/out/agentpack/paths.js +0 -498
  94. package/out/agentpack/publish.js +0 -248
  95. package/out/agentpack/stores.js +0 -554
  96. package/out/analytics/dashboard/fleetStatusUtils.js +0 -30
  97. package/out/analytics/dashboard/fleetTitleUtils.js +0 -68
  98. package/out/analytics/dashboard/refreshPolicy.js +0 -16
  99. package/out/analytics/dashboard/scheduledFleetFingerprint.js +0 -22
  100. package/out/analytics/dashboard/scheduledFleetUtils.js +0 -22
  101. package/out/analytics/dashboard/sessionEventFleetStats.js +0 -118
  102. package/out/analytics/dashboard/viewsConfigMerge.js +0 -221
  103. package/out/analytics/dashboard/webview/codicon.ttf +0 -0
  104. package/out/analytics/identity.js +0 -0
  105. package/out/analytics/sessionEvents.js +0 -323
  106. package/out/auth/callbackUriParse.js +0 -59
  107. package/out/auth/types.js +0 -12
  108. package/out/auth-broker/config.js +0 -112
  109. package/out/auth-broker/jwt.js +0 -105
  110. package/out/auth-broker/pkce.js +0 -59
  111. package/out/auth-broker/server.js +0 -677
  112. package/out/auth-broker/store.js +0 -275
  113. package/out/authority/warrant.js +0 -206
  114. package/out/base-prompts/mcp-servers/.mcp.json +0 -13
  115. package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +0 -142
  116. package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +0 -13
  117. package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +0 -1192
  118. package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +0 -23
  119. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +0 -12
  120. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +0 -102
  121. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +0 -137
  122. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +0 -118
  123. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +0 -209
  124. package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +0 -431
  125. package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +0 -270
  126. package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +0 -18
  127. package/out/base-prompts/rules/AGENTS.md +0 -204
  128. package/out/base-prompts/skills/agent-browser/SKILL.md +0 -102
  129. package/out/base-prompts/skills/agent-builder/SKILL.md +0 -73
  130. package/out/base-prompts/skills/agent-builder/references/agent-pack.md +0 -63
  131. package/out/base-prompts/skills/agent-builder/references/crewai.md +0 -66
  132. package/out/base-prompts/skills/agent-builder/references/langchain.md +0 -60
  133. package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +0 -73
  134. package/out/base-prompts/skills/agent-engine-creator/SKILL.md +0 -586
  135. package/out/base-prompts/skills/agent-engine-installer/SKILL.md +0 -106
  136. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +0 -259
  137. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +0 -189
  138. package/out/base-prompts/skills/agenthippo-cli/SKILL.md +0 -69
  139. package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +0 -63
  140. package/out/base-prompts/skills/agentpack-creator/SKILL.md +0 -375
  141. package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +0 -261
  142. package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +0 -195
  143. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +0 -391
  144. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +0 -294
  145. package/out/base-prompts/skills/agentpack-testbench/SKILL.md +0 -135
  146. package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +0 -202
  147. package/out/base-prompts/skills/algorithmic-art/SKILL.md +0 -405
  148. package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +0 -223
  149. package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +0 -599
  150. package/out/base-prompts/skills/debug-agent/SKILL.md +0 -83
  151. package/out/base-prompts/skills/desktop-automation/SKILL.md +0 -166
  152. package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +0 -441
  153. package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +0 -280
  154. package/out/base-prompts/skills/docx/LICENSE.txt +0 -30
  155. package/out/base-prompts/skills/docx/SKILL.md +0 -197
  156. package/out/base-prompts/skills/docx/docx-js.md +0 -350
  157. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  158. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  159. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  160. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  161. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  162. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  163. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  164. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  165. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  166. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  167. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  168. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  169. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  170. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  171. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  172. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  173. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  174. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  175. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  176. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  177. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  178. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  179. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  180. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  181. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  182. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  183. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  184. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  185. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  186. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  187. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  188. package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  189. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  190. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  191. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  192. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  193. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  194. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  195. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  196. package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +0 -159
  197. package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +0 -29
  198. package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +0 -69
  199. package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  200. package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +0 -951
  201. package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  202. package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  203. package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  204. package/out/base-prompts/skills/docx/ooxml.md +0 -610
  205. package/out/base-prompts/skills/docx/scripts/__init__.py +0 -1
  206. package/out/base-prompts/skills/docx/scripts/document.py +0 -1276
  207. package/out/base-prompts/skills/docx/scripts/templates/comments.xml +0 -3
  208. package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  209. package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  210. package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +0 -3
  211. package/out/base-prompts/skills/docx/scripts/templates/people.xml +0 -3
  212. package/out/base-prompts/skills/docx/scripts/utilities.py +0 -374
  213. package/out/base-prompts/skills/gateway-outbound/SKILL.md +0 -136
  214. package/out/base-prompts/skills/mcp-builder/LICENSE.txt +0 -202
  215. package/out/base-prompts/skills/mcp-builder/SKILL.md +0 -328
  216. package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +0 -602
  217. package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  218. package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  219. package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  220. package/out/base-prompts/skills/mcp-builder/scripts/connections.py +0 -151
  221. package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +0 -373
  222. package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  223. package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +0 -2
  224. package/out/base-prompts/skills/openclaw/SKILL.md +0 -406
  225. package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +0 -61
  226. package/out/base-prompts/skills/pdf/LICENSE.txt +0 -30
  227. package/out/base-prompts/skills/pdf/SKILL.md +0 -294
  228. package/out/base-prompts/skills/pdf/forms.md +0 -205
  229. package/out/base-prompts/skills/pdf/reference.md +0 -612
  230. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  231. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  232. package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +0 -12
  233. package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  234. package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +0 -41
  235. package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +0 -152
  236. package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  237. package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  238. package/out/base-prompts/skills/pptx/LICENSE.txt +0 -30
  239. package/out/base-prompts/skills/pptx/SKILL.md +0 -484
  240. package/out/base-prompts/skills/pptx/html2pptx.md +0 -625
  241. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  242. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  243. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  244. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  245. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  246. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  247. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  248. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  249. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  250. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  251. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  252. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  253. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  254. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  255. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  256. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  257. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  258. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  259. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  260. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  261. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  262. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  263. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  264. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  265. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  266. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  267. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  268. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  269. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  270. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  271. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  272. package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  273. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  274. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  275. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  276. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  277. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  278. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  279. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  280. package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +0 -159
  281. package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +0 -29
  282. package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +0 -69
  283. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  284. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  285. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  286. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  287. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  288. package/out/base-prompts/skills/pptx/ooxml.md +0 -427
  289. package/out/base-prompts/skills/pptx/scripts/html2pptx.js +0 -984
  290. package/out/base-prompts/skills/pptx/scripts/inventory.py +0 -1020
  291. package/out/base-prompts/skills/pptx/scripts/rearrange.py +0 -231
  292. package/out/base-prompts/skills/pptx/scripts/replace.py +0 -385
  293. package/out/base-prompts/skills/pptx/scripts/thumbnail.py +0 -450
  294. package/out/base-prompts/skills/prompt-lab/SKILL.md +0 -149
  295. package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +0 -2551
  296. package/out/base-prompts/skills/skill-creator/LICENSE.txt +0 -202
  297. package/out/base-prompts/skills/skill-creator/SKILL.md +0 -371
  298. package/out/base-prompts/skills/skill-creator/references/output-patterns.md +0 -82
  299. package/out/base-prompts/skills/skill-creator/references/workflows.md +0 -28
  300. package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +0 -303
  301. package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +0 -110
  302. package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +0 -95
  303. package/out/base-prompts/skills/skill-installer/SKILL.md +0 -251
  304. package/out/base-prompts/skills/skill-installer/references/store-api.md +0 -219
  305. package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +0 -83
  306. package/out/base-prompts/skills/spotlight-insights/SKILL.md +0 -101
  307. package/out/base-prompts/skills/spotlight-optimize/SKILL.md +0 -93
  308. package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +0 -2551
  309. package/out/base-prompts/skills/spotlight-query/SKILL.md +0 -314
  310. package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +0 -163
  311. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +0 -2551
  312. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +0 -744
  313. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +0 -14
  314. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +0 -7
  315. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +0 -5
  316. package/out/base-prompts/skills/spotlight-report/SKILL.md +0 -86
  317. package/out/base-prompts/skills/spotlight-visualize/SKILL.md +0 -92
  318. package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +0 -2551
  319. package/out/base-prompts/skills/xlsx/LICENSE.txt +0 -30
  320. package/out/base-prompts/skills/xlsx/SKILL.md +0 -289
  321. package/out/base-prompts/skills/xlsx/recalc.py +0 -178
  322. package/out/branding.js +0 -49
  323. package/out/cli/commands/anywhere.js +0 -66
  324. package/out/cli/commands/ask.js +0 -246
  325. package/out/cli/commands/auth-broker.js +0 -24
  326. package/out/cli/commands/auth.js +0 -219
  327. package/out/cli/commands/chat.js +0 -357
  328. package/out/cli/commands/conformance.js +0 -249
  329. package/out/cli/commands/data-plane.js +0 -67
  330. package/out/cli/commands/deploy.js +0 -263
  331. package/out/cli/commands/gateway.js +0 -218
  332. package/out/cli/commands/idp.js +0 -85
  333. package/out/cli/commands/pack.js +0 -399
  334. package/out/cli/commands/probe-custom-engine.js +0 -82
  335. package/out/cli/commands/sandbox.js +0 -332
  336. package/out/cli/commands/serve.js +0 -1758
  337. package/out/cli/commands/store.js +0 -927
  338. package/out/cli/credential-context.js +0 -91
  339. package/out/cli/data-plane/broker.js +0 -78
  340. package/out/cli/data-plane/connector-host.js +0 -121
  341. package/out/cli/data-plane/egress.js +0 -189
  342. package/out/cli/data-plane/gateway.js +0 -218
  343. package/out/cli/data-plane/index.js +0 -152
  344. package/out/cli/data-plane/registry.js +0 -89
  345. package/out/cli/data-plane/remote.js +0 -106
  346. package/out/cli/data-plane/sidecar.js +0 -161
  347. package/out/cli/deploy/blueprints/registry.js +0 -84
  348. package/out/cli/deploy/layers/identity.js +0 -172
  349. package/out/cli/deploy-init.js +0 -3023
  350. package/out/cli/deps.js +0 -342
  351. package/out/cli/idp-init.js +0 -338
  352. package/out/cli/pack-install-ref.js +0 -15
  353. package/out/cli/pack-loader.js +0 -418
  354. package/out/cli/pack-runtime.js +0 -61
  355. package/out/cli/runtime.js +0 -450
  356. package/out/cli/serve-hardening.js +0 -607
  357. package/out/cli/state-sync.js +0 -424
  358. package/out/cli/utils.js +0 -231
  359. package/out/engines/base.js +0 -188
  360. package/out/engines/claude.js +0 -1254
  361. package/out/engines/codex.js +0 -862
  362. package/out/engines/custom.js +0 -674
  363. package/out/engines/customEngineIpc.js +0 -95
  364. package/out/engines/customEngineRunnerEmbed.js +0 -107
  365. package/out/engines/customRegistry.js +0 -274
  366. package/out/engines/embeddedCustomEngineRunner.generated.js +0 -9
  367. package/out/engines/engineApiKey.js +0 -138
  368. package/out/engines/install.js +0 -372
  369. package/out/engines/persistentEngineWorker.js +0 -320
  370. package/out/engines/publish.js +0 -341
  371. package/out/evidence/sessionShip.js +0 -610
  372. package/out/gateway/bridge.js +0 -843
  373. package/out/gateway/cli-agent-id.js +0 -36
  374. package/out/gateway/client.js +0 -336
  375. package/out/gateway/client.test.js +0 -395
  376. package/out/gateway/gateway-scope-errors.js +0 -14
  377. package/out/gateway/gatewayBackup.js +0 -76
  378. package/out/gateway/installSource.js +0 -68
  379. package/out/gateway/lifecycle.js +0 -3190
  380. package/out/gateway/openclaw-channel-routing.js +0 -317
  381. package/out/gateway/outboundHandles.js +0 -373
  382. package/out/gateway/outboundPaths.js +0 -75
  383. package/out/gateway/outboundSend.js +0 -254
  384. package/out/gateway/protocol.js +0 -48
  385. package/out/gateway/secretsPreflight.js +0 -90
  386. package/out/gateway/sessionRouting.js +0 -77
  387. package/out/headless/index.js +0 -539
  388. package/out/headless/sync.js +0 -473
  389. package/out/live-e2e/mcp-demo/server.mjs +0 -32
  390. package/out/live-probe/connector.mjs +0 -8
  391. package/out/live-probe/env-report.mjs +0 -8
  392. package/out/mcp.js +0 -654
  393. package/out/pack/caps.js +0 -271
  394. package/out/pack/governor.js +0 -309
  395. package/out/pack/lease-schema.js +0 -270
  396. package/out/pack/lease.js +0 -640
  397. package/out/pack/reconcile.js +0 -217
  398. package/out/pack/signature.js +0 -839
  399. package/out/permissions.js +0 -108
  400. package/out/permissions.mapping.yaml +0 -68
  401. package/out/registry/index.js +0 -332
  402. package/out/runner/host.js +0 -131
  403. package/out/runner/protocol.js +0 -108
  404. package/out/runner/remoteEngine.js +0 -130
  405. package/out/runtime/interfaces/config-provider.js +0 -32
  406. package/out/runtime/interfaces/edit-tracker.js +0 -24
  407. package/out/runtime/interfaces/index.js +0 -29
  408. package/out/runtime/interfaces/logger.js +0 -40
  409. package/out/runtime/interfaces/permission-handler.js +0 -66
  410. package/out/runtime/interfaces/session-store.js +0 -342
  411. package/out/runtime/vscode/vscode-session-store.js +0 -22
  412. package/out/sandbox/containerDriver.js +0 -570
  413. package/out/sandbox/processDriver.js +0 -315
  414. package/out/sandbox/sandboxRunner.js +0 -191
  415. package/out/sandbox/support.js +0 -301
  416. package/out/sandbox/types.js +0 -55
  417. package/out/scripts/analytics/fleet-local-adapter.js +0 -246
  418. package/out/scripts/analytics/live-fleet-4-collector.cjs +0 -315
  419. package/out/scripts/analytics/live-fleet-4-daemon.mjs +0 -134
  420. package/out/scripts/analytics/live-fleet-4-widget.cjs +0 -433
  421. package/out/scripts/gateway/Dockerfile.serve +0 -45
  422. package/out/scripts/gateway/docker-serve.sh +0 -248
  423. package/out/scripts/gateway/setup-gateway-server.ps1 +0 -953
  424. package/out/scripts/gateway/setup-gateway-server.sh +0 -935
  425. package/out/session.js +0 -272
  426. package/out/shared/agentEngines.js +0 -170
  427. package/out/shared/index.js +0 -109
  428. package/out/shared/models.js +0 -136
  429. package/out/shared/providers.js +0 -205
  430. package/out/spotlight/install.js +0 -345
  431. package/out/spotlight/publish.js +0 -544
  432. package/out/spotlight/yaml.js +0 -31
  433. package/out/store/index.js +0 -786
  434. package/out/store/shared.js +0 -241
  435. package/out/test/agentEnvFile.test.js +0 -97
  436. package/out/test/agentYaml.test.js +0 -373
  437. package/out/test/agentpack.test.js +0 -639
  438. package/out/test/analytics-lifecycle.test.js +0 -174
  439. package/out/test/analytics.test.js +0 -756
  440. package/out/test/auth-baseurl.test.js +0 -132
  441. package/out/test/auth-bootstrapApply.test.js +0 -123
  442. package/out/test/auth-broker-hardening.test.js +0 -227
  443. package/out/test/auth-broker.test.js +0 -541
  444. package/out/test/auth-callback.test.js +0 -62
  445. package/out/test/blueprint-registry.test.js +0 -105
  446. package/out/test/branding.test.js +0 -242
  447. package/out/test/caps-governor.test.js +0 -455
  448. package/out/test/cli-agent-id.test.js +0 -55
  449. package/out/test/codex-new-file-survival.test.js +0 -119
  450. package/out/test/configuration-scopes.test.js +0 -131
  451. package/out/test/credential-context.test.js +0 -160
  452. package/out/test/custom-env-layering.test.js +0 -151
  453. package/out/test/customEngineInstall.test.js +0 -100
  454. package/out/test/customRegistry.test.js +0 -122
  455. package/out/test/data-plane-engine-wiring.test.js +0 -250
  456. package/out/test/data-plane.test.js +0 -311
  457. package/out/test/deploy-init.test.js +0 -854
  458. package/out/test/deps-auto-install.test.js +0 -81
  459. package/out/test/egress-broker.test.js +0 -172
  460. package/out/test/engine-integration.test.js +0 -297
  461. package/out/test/enginePaths.test.js +0 -195
  462. package/out/test/enginePublish.test.js +0 -82
  463. package/out/test/fleetDisplayUtils.test.js +0 -100
  464. package/out/test/gateway-client.test.js +0 -409
  465. package/out/test/gateway-install-source.test.js +0 -111
  466. package/out/test/gateway-lifecycle.test.js +0 -608
  467. package/out/test/gateway-outbound.test.js +0 -538
  468. package/out/test/gateway-reset-mappings.test.js +0 -221
  469. package/out/test/gateway-route-index.test.js +0 -235
  470. package/out/test/gateway-secrets-preflight.test.js +0 -127
  471. package/out/test/gateway-session-routing.test.js +0 -65
  472. package/out/test/gateway-session-store.test.js +0 -216
  473. package/out/test/github.test.js +0 -232
  474. package/out/test/governor-hardening.test.js +0 -217
  475. package/out/test/headless-sync.test.js +0 -301
  476. package/out/test/helpers/manifest-fixtures.js +0 -27
  477. package/out/test/idp-init.test.js +0 -151
  478. package/out/test/lease-signature.test.js +0 -357
  479. package/out/test/lease-trust-root.test.js +0 -273
  480. package/out/test/lease-validation.test.js +0 -169
  481. package/out/test/lease.test.js +0 -114
  482. package/out/test/mcp-obo.test.js +0 -107
  483. package/out/test/mcp.test.js +0 -389
  484. package/out/test/model-routing.test.js +0 -133
  485. package/out/test/openclaw-channel-routing.test.js +0 -191
  486. package/out/test/pack-admission.test.js +0 -216
  487. package/out/test/pack-digest-parity.test.js +0 -140
  488. package/out/test/pack-reconcile.test.js +0 -183
  489. package/out/test/pack-signature-artifact.test.js +0 -103
  490. package/out/test/pack-signature.test.js +0 -176
  491. package/out/test/pack-trust-root.test.js +0 -202
  492. package/out/test/pathSafety.test.js +0 -63
  493. package/out/test/permissions.test.js +0 -175
  494. package/out/test/persistent-engine-worker.test.js +0 -220
  495. package/out/test/refreshPolicy.test.js +0 -60
  496. package/out/test/registry.test.js +0 -351
  497. package/out/test/remote-trust.test.js +0 -242
  498. package/out/test/runner-protocol.test.js +0 -249
  499. package/out/test/runtime.test.js +0 -349
  500. package/out/test/sandbox-driver.test.js +0 -553
  501. package/out/test/sandbox-hardening.test.js +0 -395
  502. package/out/test/sandbox-runner.test.js +0 -343
  503. package/out/test/scheduledFleetStorage.test.js +0 -72
  504. package/out/test/serve-gateway-turn.test.js +0 -267
  505. package/out/test/serve-hardening.test.js +0 -418
  506. package/out/test/serveClient.test.js +0 -209
  507. package/out/test/sessionJsonlFleet.test.js +0 -78
  508. package/out/test/sessionShip.test.js +0 -560
  509. package/out/test/shared.test.js +0 -173
  510. package/out/test/skills-mcp.test.js +0 -299
  511. package/out/test/spotlightPublish.test.js +0 -327
  512. package/out/test/spotlightYaml.test.js +0 -58
  513. package/out/test/state-sync-peer.test.js +0 -156
  514. package/out/test/store-install-safe.test.js +0 -69
  515. package/out/test/store.test.js +0 -331
  516. package/out/test/stores.test.js +0 -504
  517. package/out/test/sync-switch.test.js +0 -251
  518. package/out/test/teamViewDiscovery.test.js +0 -90
  519. package/out/test/tls-bypass.test.js +0 -90
  520. package/out/test/toolPath.test.js +0 -120
  521. package/out/test/utils.test.js +0 -308
  522. package/out/test/viewsConfigMerge.test.js +0 -180
  523. package/out/test/warrant.test.js +0 -138
  524. package/out/test/windows-command-line.test.js +0 -75
  525. package/out/utils/agentEnvFile.js +0 -131
  526. package/out/utils/binary.js +0 -175
  527. package/out/utils/index.js +0 -37
  528. package/out/utils/network.js +0 -74
  529. package/out/utils/paths.js +0 -116
  530. package/out/utils/pid.js +0 -180
  531. package/out/utils/resolveNodeBinary.js +0 -168
  532. package/out/utils/windowsCommandLine.js +0 -29
  533. package/out/utils.js +0 -968
@@ -1,1505 +1,56 @@
1
- /* -----------------------------------------------------------------------------
2
- * Copyright (c) AgentHippo.ai. All rights reserved.
3
- * -----------------------------------------------------------------------------
4
- * Spotlight Dashboard - Default Panel Module
5
- * Handles artifact display, rendering, and interactions.
6
- * Uses panelApi (provided by runtime-bridge.js) instead of raw VS Code API.
7
- *
8
- * This bundled file is the ONLY panel.js ever executed (see provider.getHtmlContent).
9
- * It is not seeded to .agent-hippo/analytics/panel/ — a copy there would never run.
10
- * Dashboard UI is customized via panel.html / panel.css, which ARE read from that directory.
11
- * ----------------------------------------------------------------------------- */
12
-
13
- // @ts-check
14
- /// <reference lib="dom" />
15
-
16
- // @ts-ignore - provided by runtime-bridge.js
17
- const panelApi = window.panelApi;
18
-
19
- // Seed/version marker for deciding whether a user's locally seeded panel.js
20
- // is compatible with the extension runtime (e.g. message forwarding fixes).
21
- const AGENTIDE_PANEL_JS_VERSION = '2026.07.21';
22
- try { window.__AGENTIDE_PANEL_JS_VERSION__ = AGENTIDE_PANEL_JS_VERSION; } catch { /* no-op */ }
23
-
24
- // -----------------------------------------------------------------------------
25
- // State
26
- // -----------------------------------------------------------------------------
27
-
28
- const PANEL_ID = 'default';
29
-
30
- /** Host ↔ panel / iframe ↔ panel: view-owned export JSON live updates. */
31
- const VIEW_EXPORT_SNAPSHOT = 'viewExportSnapshot';
32
- const REQUEST_VIEW_EXPORT_SNAPSHOT = 'requestViewExportSnapshot';
33
- /** @deprecated Renamed; widgets may still emit this until updated. */
34
- const LEGACY_REQUEST_LIVE_FLEET_SNAPSHOT = 'requestLiveFleetSnapshot';
35
- const LEGACY_VIEW_EXPORT_SNAPSHOT = 'liveFleetSnapshot';
36
- const ADD_VIEW_OPTION_VALUE = '__add_view__';
37
-
38
- /**
39
- * @typedef {{ order: number, hidden: boolean, height?: number, expanded?: boolean }} PanelChartState
40
- * @typedef {{ version: 1, charts: Record<string, PanelChartState>, summaryHidden?: boolean }} PanelState
41
- */
42
-
43
- /** @returns {PanelState} */
44
- function createDefaultPanelState() {
45
- return { version: 1, charts: {}, summaryHidden: false };
46
- }
47
-
48
- /** @type {{ artifacts: object[], galleryIgnorePatterns: string[], summaryCards: object[] | null, manifest: object | null, isRefreshing: boolean, scope: 'global'|'workspace', projectId: string, agentId: string, agentIds: string[], views: Array<{ id: string, name: string, scope: 'global'|'workspace', system: boolean, layout?: 'fleet-dashboard'|'gallery', refreshOnNavigate?: 'always'|'when-empty', preserveFleetIframe?: boolean }>, activeViewId: string, disabledCharts: Set<string>, panelState: PanelState, panelStateLoaded: boolean, lastArtifactCatalogSignature: string, fleetAutoRefreshIntervalSeconds: number, fleetRefreshVersion: number, needsGalleryRender: boolean, pendingForceFreshHtml: boolean, refreshingViewId: string | null }} */
49
- const state = {
50
- artifacts: [],
51
- galleryIgnorePatterns: [],
52
- summaryCards: null,
53
- manifest: null,
54
- isRefreshing: false,
55
- scope: 'global',
56
- projectId: '',
57
- agentId: '',
58
- agentIds: [],
59
- views: [],
60
- activeViewId: 'global',
61
- disabledCharts: new Set(),
62
- panelState: createDefaultPanelState(),
63
- panelStateLoaded: false,
64
- lastArtifactCatalogSignature: '',
65
- fleetAutoRefreshIntervalSeconds: 30,
66
- fleetRefreshVersion: 0,
67
- surfaceMode: 'spotlight',
68
- // While a refresh is in flight, multiple artifactsUpdated bursts can arrive as scripts/
69
- // watchers write files incrementally. Rendering on each one tears down and rebuilds the
70
- // whole gallery (see renderGallery), which is visibly flickery. Defer to one render when
71
- // the refresh actually ends (refreshFinished/refreshFailed/refreshNoData).
72
- needsGalleryRender: false,
73
- pendingForceFreshHtml: false,
74
- // View id the in-flight refresh belongs to. Termination messages carry the viewId captured
75
- // at refresh start, so after a mid-refresh view switch they no longer match activeViewId —
76
- // match against this instead so isRefreshing cannot get stuck true.
77
- refreshingViewId: null,
78
- };
79
-
80
- /** Render the gallery now if a render was deferred mid-refresh. */
81
- function flushPendingGalleryRender() {
82
- if (!state.needsGalleryRender) {
83
- return;
84
- }
85
- state.needsGalleryRender = false;
86
- const forceFreshHtml = state.pendingForceFreshHtml;
87
- state.pendingForceFreshHtml = false;
88
- renderGallery(state.artifacts, { forceFreshHtml });
89
- }
90
-
91
- /**
92
- * Single entry point for gallery renders triggered by host messages. While a refresh is in
93
- * flight, defer to one render at refresh termination instead of tearing down and rebuilding
94
- * the gallery on every artifactsUpdated/manifestUpdated/disabledChartsUpdated burst.
95
- * @param {{ forceFreshHtml?: boolean }} [options]
96
- */
97
- function requestGalleryRender(options) {
98
- if (state.isRefreshing) {
99
- state.needsGalleryRender = true;
100
- if (options?.forceFreshHtml) {
101
- state.pendingForceFreshHtml = true;
102
- }
103
- return;
104
- }
105
- renderGallery(state.artifacts, { forceFreshHtml: Boolean(options?.forceFreshHtml) });
106
- }
107
-
108
- /**
109
- * The active view changed while a refresh may be in flight: that refresh belongs to the view
110
- * being left, so stop tracking it. The abandoned refresh's termination message is dropped by
111
- * the viewId gates, and without this reset a deferred render would block the new view's first
112
- * paint until that refresh ends (or forever, if no matching termination arrives).
113
- */
114
- function resetRefreshTrackingForViewChange() {
115
- if (!state.isRefreshing && !state.needsGalleryRender && !state.pendingForceFreshHtml) {
116
- return;
117
- }
118
- state.isRefreshing = false;
119
- state.refreshingViewId = null;
120
- state.needsGalleryRender = false;
121
- state.pendingForceFreshHtml = false;
122
- setRefreshButtonState(false);
123
- setPerChartRefreshing(false);
124
- }
125
-
126
- function applySurfaceMode(mode) {
127
- const team = mode === 'team';
128
- state.surfaceMode = team ? 'team' : 'spotlight';
129
- document.body.classList.toggle('spotlight-surface-team', team);
130
- applyTeamViewSelectVisibility();
131
- }
132
-
133
- /**
134
- * Fleet view without preserveFleetIframe: host bumps fleetRefreshVersion and this panel reloads agent-fleet.html.
135
- * preserveFleetIframe views receive viewExportSnapshot postMessages instead.
136
- * Session/export JSON shape: extensions/agentide/src/analytics/dashboard/FLEET_DATA_CONTRACT.md
137
- */
138
- /** Timer for Fleet view auto-refresh; cleared when view changes or interval is 0. */
139
- let fleetAutoRefreshTimerId = null;
140
-
141
- /**
142
- * Only auto-refresh Fleet when the user is at the top of the widget.
143
- * We check both the outer card scroll container and the embedded iframe
144
- * document scroll position because either can own scrolling depending on
145
- * current layout/height.
146
- * @returns {boolean}
147
- */
148
- function isFleetWidgetAtTop() {
149
- const fleetCard = /** @type {HTMLElement | null} */ (document.querySelector('.fleet-dashboard-full.artifact-card'));
150
- if (!fleetCard) {
151
- return true;
152
- }
153
-
154
- const contentEl = /** @type {HTMLElement | null} */ (fleetCard.querySelector('.artifact-content'));
155
- if (contentEl && contentEl.scrollTop > 4) {
156
- return false;
157
- }
158
-
159
- const iframe = /** @type {HTMLIFrameElement | null} */ (fleetCard.querySelector('.html-preview iframe'));
160
- if (!iframe) {
161
- return true;
162
- }
163
-
164
- try {
165
- const iframeDoc = iframe.contentDocument;
166
- const bodyTop = iframeDoc?.body?.scrollTop ?? 0;
167
- const docTop = iframeDoc?.documentElement?.scrollTop ?? 0;
168
- const winTop = iframe.contentWindow?.scrollY ?? 0;
169
- return bodyTop <= 4 && docTop <= 4 && winTop <= 4;
170
- } catch {
171
- // If iframe scroll is unavailable, avoid blocking auto-refresh.
172
- return true;
173
- }
174
- }
175
-
176
- /**
177
- * Start or stop Fleet auto-refresh based on current view and configured interval.
178
- * Call after activeViewId or fleetAutoRefreshIntervalSeconds changes.
179
- */
180
- /** @returns {{ id: string, name: string, scope: 'global'|'workspace', system: boolean, layout?: string, refreshOnNavigate?: string, preserveFleetIframe?: boolean } | undefined} */
181
- function getActiveViewConfig() {
182
- return state.views.find(view => view.id === state.activeViewId);
183
- }
184
-
185
- function activeViewUsesFleetDashboardLayout() {
186
- return getActiveViewConfig()?.layout === 'fleet-dashboard';
187
- }
188
-
189
- function activeViewPreservesFleetIframe() {
190
- return getActiveViewConfig()?.preserveFleetIframe === true;
191
- }
192
-
193
- /**
194
- * Iframe-only snapshot views — no gallery Open/Delete footer.
195
- * Config-driven via `preserveFleetIframe` (covers Workflows and any fleet-dashboard view).
196
- * The id pattern is a back-compat fallback for legacy live-fleet views whose view.yaml
197
- * predates the flag and derives `preserveFleetIframe === false` host-side.
198
- */
199
- function activeViewIsLiveFleetSnapshot() {
200
- if (activeViewPreservesFleetIframe()) {
201
- return true;
202
- }
203
- return /^live-fleet-\d+$/.test(String(state.activeViewId || ''));
204
- }
205
-
206
- function applyFleetAutoRefresh() {
207
- if (fleetAutoRefreshTimerId !== null) {
208
- clearInterval(fleetAutoRefreshTimerId);
209
- fleetAutoRefreshTimerId = null;
210
- }
211
- const intervalSec = state.fleetAutoRefreshIntervalSeconds;
212
- const isLiveFleetView = state.activeViewId === 'fleet' || activeViewPreservesFleetIframe();
213
- if (isLiveFleetView && typeof intervalSec === 'number' && intervalSec > 0) {
214
- const ms = intervalSec * 1000;
215
- fleetAutoRefreshTimerId = setInterval(() => {
216
- if (!isFleetWidgetAtTop()) {
217
- return;
218
- }
219
- if (activeViewPreservesFleetIframe()) {
220
- panelApi.postMessage({ type: REQUEST_VIEW_EXPORT_SNAPSHOT });
221
- return;
222
- }
223
- panelApi.postMessage({ type: 'refreshView' });
224
- }, ms);
225
- }
226
- }
227
-
228
- function applyTeamViewSelectVisibility() {
229
- const viewSelect = /** @type {HTMLSelectElement | null} */ (document.getElementById('view-select'));
230
- if (!viewSelect) {
231
- return;
232
- }
233
- if (state.surfaceMode === 'team') {
234
- if (state.views.length > 1) {
235
- viewSelect.style.setProperty('display', 'inline-block', 'important');
236
- } else {
237
- viewSelect.style.setProperty('display', 'none', 'important');
238
- }
239
- } else {
240
- viewSelect.style.removeProperty('display');
241
- }
242
- }
243
-
244
- function applyViewOptions() {
245
- const viewSelect = /** @type {HTMLSelectElement | null} */ (document.getElementById('view-select'));
246
- if (!viewSelect) {
247
- return;
248
- }
249
- const previous = state.activeViewId || viewSelect.value || '';
250
- viewSelect.innerHTML = '';
251
-
252
- if (state.surfaceMode !== 'team') {
253
- const addViewOption = document.createElement('option');
254
- addViewOption.value = ADD_VIEW_OPTION_VALUE;
255
- addViewOption.textContent = '+ Add view…';
256
- viewSelect.appendChild(addViewOption);
257
- }
258
-
259
- for (const view of state.views) {
260
- const option = document.createElement('option');
261
- option.value = view.id;
262
- option.textContent = view.name;
263
- viewSelect.appendChild(option);
264
- }
265
- const next = state.views.some((view) => view.id === state.activeViewId)
266
- ? state.activeViewId
267
- : previous;
268
- if (next && state.views.some((view) => view.id === next)) {
269
- viewSelect.value = next;
270
- state.activeViewId = next;
271
- }
272
- applyTeamViewSelectVisibility();
273
- applyShareButtonState();
274
- }
275
-
276
- const SHARE_ICON_HTML = '<span class="btn-icon" aria-hidden="true">&#8593;</span>';
277
- const INSTALL_ICON_HTML = '<span class="btn-icon" aria-hidden="true">&#8595;</span>';
278
-
279
- function onInstallViewClick() {
280
- panelApi.postMessage({ type: 'installView' });
281
- }
282
-
283
- function onShareViewClick() {
284
- const activeView = getActiveView();
285
- // Always forward to the extension host so built-in views get the explanatory toast.
286
- panelApi.postMessage({
287
- type: 'shareView',
288
- viewId: activeView?.id ?? '',
289
- bundle: 'full',
290
- });
291
- }
292
-
293
- function onShareScriptsOnlyClick() {
294
- const activeView = getActiveView();
295
- panelApi.postMessage({
296
- type: 'shareView',
297
- viewId: activeView?.id ?? '',
298
- bundle: 'scripts-only',
299
- });
300
- }
301
-
302
- /**
303
- * Insert share toolbar control when user's panel.html predates the share button.
304
- */
305
- function ensureShareToolbarButton() {
306
- let shareBtn = document.getElementById('share-view-btn');
307
- if (!shareBtn) {
308
- const refreshBtn = document.getElementById('refresh-charts-btn');
309
- if (!refreshBtn?.parentElement) {
310
- return;
311
- }
312
- let iconGroup = refreshBtn.closest('.toolbar-icon-group');
313
- if (!iconGroup) {
314
- iconGroup = document.createElement('div');
315
- iconGroup.className = 'toolbar-icon-group';
316
- refreshBtn.parentElement.insertBefore(iconGroup, refreshBtn);
317
- iconGroup.appendChild(refreshBtn);
318
- }
319
- shareBtn = document.createElement('button');
320
- shareBtn.id = 'share-view-btn';
321
- shareBtn.className = 'toolbar-btn icon-only';
322
- shareBtn.title = 'Publish current view to store (full bundle)';
323
- shareBtn.setAttribute('aria-label', shareBtn.title);
324
- shareBtn.innerHTML = SHARE_ICON_HTML;
325
- iconGroup.appendChild(shareBtn);
326
- } else {
327
- const refreshBtn = document.getElementById('refresh-charts-btn');
328
- const icon = shareBtn.querySelector('.btn-icon');
329
- if (icon && icon.textContent !== '↑') {
330
- icon.outerHTML = SHARE_ICON_HTML;
331
- }
332
- if (refreshBtn && shareBtn.compareDocumentPosition(refreshBtn) & Node.DOCUMENT_POSITION_FOLLOWING) {
333
- refreshBtn.insertAdjacentElement('afterend', shareBtn);
334
- }
335
- }
336
- const boundShareBtn = document.getElementById('share-view-btn');
337
- if (boundShareBtn && !boundShareBtn.dataset.boundShareClick) {
338
- boundShareBtn.dataset.boundShareClick = '1';
339
- boundShareBtn.addEventListener('click', onShareViewClick);
340
- }
341
- applyShareButtonState();
342
- ensureInstallToolbarButton();
343
- }
344
-
345
- /**
346
- * Insert install toolbar control when user's panel.html predates the install button.
347
- */
348
- function ensureInstallToolbarButton() {
349
- let installBtn = document.getElementById('install-view-btn');
350
- if (!installBtn) {
351
- const shareBtn = document.getElementById('share-view-btn');
352
- if (!shareBtn?.parentElement) {
353
- return;
354
- }
355
- let iconGroup = shareBtn.closest('.toolbar-icon-group');
356
- if (!iconGroup) {
357
- iconGroup = document.createElement('div');
358
- iconGroup.className = 'toolbar-icon-group';
359
- shareBtn.parentElement.insertBefore(iconGroup, shareBtn);
360
- iconGroup.appendChild(shareBtn);
361
- }
362
- installBtn = document.createElement('button');
363
- installBtn.id = 'install-view-btn';
364
- installBtn.className = 'toolbar-btn icon-only';
365
- installBtn.title = 'Install Spotlight view from store';
366
- installBtn.setAttribute('aria-label', installBtn.title);
367
- installBtn.innerHTML = INSTALL_ICON_HTML;
368
- shareBtn.insertAdjacentElement('afterend', installBtn);
369
- } else {
370
- const shareBtn = document.getElementById('share-view-btn');
371
- const icon = installBtn.querySelector('.btn-icon');
372
- if (icon && icon.textContent !== '↓') {
373
- icon.outerHTML = INSTALL_ICON_HTML;
374
- }
375
- if (shareBtn && installBtn.compareDocumentPosition(shareBtn) & Node.DOCUMENT_POSITION_FOLLOWING) {
376
- shareBtn.insertAdjacentElement('afterend', installBtn);
377
- }
378
- }
379
- const boundInstallBtn = document.getElementById('install-view-btn');
380
- if (boundInstallBtn && !boundInstallBtn.dataset.boundInstallClick) {
381
- boundInstallBtn.dataset.boundInstallClick = '1';
382
- boundInstallBtn.addEventListener('click', onInstallViewClick);
383
- }
384
- }
385
-
386
- function applyShareButtonState() {
387
- const shareBtn = document.getElementById('share-view-btn');
388
- if (!shareBtn) {
389
- return;
390
- }
391
- const activeView = getActiveView();
392
- const canShare = Boolean(activeView && !activeView.system);
393
- shareBtn.classList.toggle('share-unavailable', !canShare);
394
- shareBtn.title = canShare
395
- ? 'Publish current view to store (full bundle)'
396
- : 'Built-in views cannot be shared (duplicate or create a custom view)';
397
- shareBtn.setAttribute('aria-label', shareBtn.title);
398
-
399
- const scriptsOnlyItem = document.getElementById('share-scripts-only-menu-item');
400
- if (scriptsOnlyItem) {
401
- // Keep clickable so the host can show the same built-in-view explanation.
402
- scriptsOnlyItem.disabled = false;
403
- scriptsOnlyItem.classList.toggle('share-unavailable', !canShare);
404
- scriptsOnlyItem.title = canShare
405
- ? 'Publish widget scripts only (no charts or exports)'
406
- : 'Built-in views cannot be shared (duplicate or create a custom view)';
407
- }
408
- }
409
-
410
- function getActiveView() {
411
- return state.views.find((view) => view.id === state.activeViewId);
412
- }
413
-
414
- /**
415
- * @param {string} nextViewId
416
- */
417
- function switchToView(nextViewId) {
418
- if (!nextViewId || nextViewId === state.activeViewId) {
419
- return;
420
- }
421
- state.activeViewId = nextViewId;
422
- resetRefreshTrackingForViewChange();
423
- state.panelStateLoaded = false;
424
- state.panelState = createDefaultPanelState();
425
- const viewSelect = /** @type {HTMLSelectElement | null} */ (document.getElementById('view-select'));
426
- if (viewSelect && state.views.some((view) => view.id === nextViewId)) {
427
- viewSelect.value = nextViewId;
428
- }
429
- applyFleetAutoRefresh();
430
- applyShareButtonState();
431
- panelApi.postMessage({ type: 'setActiveView', viewId: nextViewId });
432
- panelApi.postMessage({ type: 'requestPanelState', panelId: PANEL_ID });
433
- }
434
-
435
- /**
436
- * Show the "New view name" modal. Used instead of prompt() because
437
- * window.prompt() is not available in VS Code webviews.
438
- * @param {() => void} [onResetSelect] - Optional callback to reset view dropdown (e.g. when opened from "+ Add view…").
439
- */
440
- function showNewViewModal(onResetSelect) {
441
- const overlay = document.getElementById('new-view-modal');
442
- const input = document.getElementById('new-view-name-input');
443
- const okBtn = document.getElementById('new-view-modal-ok');
444
- const cancelBtn = document.getElementById('new-view-modal-cancel');
445
- if (!overlay || !input || !okBtn || !cancelBtn) {
446
- return;
447
- }
448
- if (typeof onResetSelect === 'function') {
449
- onResetSelect();
450
- }
451
- input.value = '';
452
- overlay.style.display = 'flex';
453
- overlay.setAttribute('aria-hidden', 'false');
454
- input.focus();
455
-
456
- function hide() {
457
- overlay.style.display = 'none';
458
- overlay.setAttribute('aria-hidden', 'true');
459
- overlay.removeEventListener('click', handleOverlayClick);
460
- document.removeEventListener('keydown', handleKeydown);
461
- }
462
-
463
- function submit() {
464
- const trimmed = input.value.trim();
465
- hide();
466
- if (trimmed) {
467
- panelApi.postMessage({ type: 'createView', name: trimmed });
468
- }
469
- }
470
-
471
- function handleOverlayClick(e) {
472
- if (e.target === overlay) {
473
- hide();
474
- }
475
- }
476
-
477
- function handleKeydown(e) {
478
- if (e.key === 'Escape') {
479
- hide();
480
- e.preventDefault();
481
- } else if (e.key === 'Enter') {
482
- submit();
483
- e.preventDefault();
484
- }
485
- }
486
-
487
- overlay.addEventListener('click', handleOverlayClick);
488
- document.addEventListener('keydown', handleKeydown);
489
-
490
- okBtn.onclick = () => submit();
491
- cancelBtn.onclick = () => hide();
492
- }
493
-
494
- function promptCreateView() {
495
- showNewViewModal();
496
- }
497
-
498
- function initViews() {
499
- const viewSelect = /** @type {HTMLSelectElement | null} */ (document.getElementById('view-select'));
500
- viewSelect?.addEventListener('change', () => {
501
- const nextViewId = viewSelect.value || '';
502
- if (!nextViewId) {
503
- return;
504
- }
505
- if (nextViewId === ADD_VIEW_OPTION_VALUE) {
506
- showNewViewModal(() => {
507
- viewSelect.value = state.activeViewId || '';
508
- });
509
- return;
510
- }
511
- if (nextViewId === state.activeViewId) {
512
- return;
513
- }
514
- switchToView(nextViewId);
515
- });
516
- }
517
-
518
- function refreshCurrentView() {
519
- const btn = document.getElementById('refresh-charts-btn');
520
- if (btn && !btn.classList.contains('refreshing')) {
521
- setRefreshButtonState(true);
522
- panelApi.postMessage({ type: 'refreshView' });
523
- }
524
- }
525
-
526
- // -----------------------------------------------------------------------------
527
- // Utility Functions
528
- // -----------------------------------------------------------------------------
529
-
530
- /**
531
- * Format a date as relative time (e.g., "2 min ago")
532
- * @param {Date} date
533
- * @returns {string}
534
- */
535
- function formatTimeAgo(date) {
536
- const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
537
- if (seconds < 60) {
538
- return 'just now';
539
- }
540
- if (seconds < 3600) {
541
- return `${Math.floor(seconds / 60)} min ago`;
542
- }
543
- if (seconds < 86400) {
544
- return `${Math.floor(seconds / 3600)}h ago`;
545
- }
546
- return `${Math.floor(seconds / 86400)}d ago`;
547
- }
548
-
549
- /**
550
- * Format file size in human-readable form
551
- * @param {number} bytes
552
- * @returns {string}
553
- */
554
- function formatSize(bytes) {
555
- if (bytes < 1024) {
556
- return `${bytes} B`;
557
- }
558
- if (bytes < 1024 * 1024) {
559
- return `${(bytes / 1024).toFixed(1)} KB`;
560
- }
561
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
562
- }
563
-
564
- /**
565
- * Escape HTML special characters
566
- * @param {string} text
567
- * @returns {string}
568
- */
569
- function escapeHtml(text) {
570
- const div = document.createElement('div');
571
- div.textContent = text;
572
- return div.innerHTML;
573
- }
574
-
575
- /**
576
- * Simple markdown rendering
577
- * @param {string} text
578
- * @returns {string}
579
- */
580
- function renderMarkdown(text) {
581
- return text
582
- .replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>')
583
- .replace(/`([^`]+)`/g, '<code>$1</code>')
584
- .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
585
- .replace(/\*(.+?)\*/g, '<em>$1</em>')
586
- .replace(/^### (.+)$/gm, '<h3>$1</h3>')
587
- .replace(/^## (.+)$/gm, '<h2>$1</h2>')
588
- .replace(/^# (.+)$/gm, '<h1>$1</h1>')
589
- .replace(/\n/g, '<br>');
590
- }
591
-
592
- /**
593
- * @param {string} value
594
- * @returns {string}
595
- */
596
- function normalizeFilename(value) {
597
- return String(value || '').trim().toLowerCase();
598
- }
599
-
600
- /**
601
- * @param {unknown} raw
602
- * @returns {PanelState}
603
- */
604
- function normalizePanelState(raw) {
605
- const parsed = raw && typeof raw === 'object' ? /** @type {Record<string, unknown>} */ (raw) : {};
606
- const rawCharts = parsed.charts && typeof parsed.charts === 'object'
607
- ? /** @type {Record<string, unknown>} */ (parsed.charts)
608
- : {};
609
- /** @type {Record<string, PanelChartState>} */
610
- const charts = {};
611
- let nextOrder = 1;
612
- for (const [key, value] of Object.entries(rawCharts)) {
613
- const filenameKey = normalizeFilename(key);
614
- if (!filenameKey) {
615
- continue;
616
- }
617
- const entry = value && typeof value === 'object' ? /** @type {Record<string, unknown>} */ (value) : {};
618
- const parsedOrder = Number(entry.order);
619
- const order = Number.isFinite(parsedOrder) && parsedOrder > 0 ? parsedOrder : nextOrder;
620
- const hidden = entry.hidden === true;
621
- const height = Number(entry.height);
622
- const expanded = entry.expanded === true;
623
- charts[filenameKey] = {
624
- order,
625
- hidden,
626
- height: Number.isFinite(height) && height >= 120 ? height : undefined,
627
- expanded,
628
- };
629
- nextOrder = Math.max(nextOrder, order + 1);
630
- }
631
- const summaryHidden = parsed.summaryHidden === true;
632
- return { version: 1, charts, summaryHidden };
633
- }
634
-
635
- /**
636
- * @returns {number}
637
- */
638
- function getNextChartOrder() {
639
- const entries = Object.values(state.panelState.charts);
640
- if (entries.length === 0) {
641
- return 1;
642
- }
643
- return entries.reduce((max, item) => Math.max(max, item.order), 0) + 1;
644
- }
645
-
646
- /**
647
- * Ensure every discovered chart has a panel-state entry.
648
- * @param {Array<object>} artifacts
649
- * @returns {boolean}
650
- */
651
- function ensurePanelStateForArtifacts(artifacts) {
652
- let changed = false;
653
- let nextOrder = getNextChartOrder();
654
- for (const artifact of artifacts) {
655
- if (artifact.type !== 'chart' && artifact.type !== 'image') {
656
- continue;
657
- }
658
- const key = normalizeFilename(String(artifact.filename || ''));
659
- if (!key) {
660
- continue;
661
- }
662
- if (!state.panelState.charts[key]) {
663
- state.panelState.charts[key] = { order: nextOrder++, hidden: false, height: undefined, expanded: false };
664
- changed = true;
665
- }
666
- }
667
- return changed;
668
- }
669
-
670
- /**
671
- * @param {string} filename
672
- * @returns {PanelChartState}
673
- */
674
- function getChartPanelState(filename) {
675
- const key = normalizeFilename(filename);
676
- if (!state.panelState.charts[key]) {
677
- state.panelState.charts[key] = { order: getNextChartOrder(), hidden: false, height: undefined, expanded: false };
678
- }
679
- return state.panelState.charts[key];
680
- }
681
-
682
- function setChartHeight(filename, heightPx) {
683
- const chartState = getChartPanelState(filename);
684
- chartState.height = heightPx >= 120 && heightPx <= 2000 ? heightPx : undefined;
685
- savePanelState();
686
- }
687
-
688
- function setChartExpanded(filename, expanded) {
689
- const chartState = getChartPanelState(filename);
690
- chartState.expanded = expanded === true;
691
- savePanelState();
692
- }
693
-
694
- /**
695
- * Auto-fit Fleet iframe content on first load when no explicit panel height
696
- * has been saved yet. This prevents an oversized empty area at the bottom
697
- * while preserving click-to-expand behavior.
698
- * @param {HTMLElement} card
699
- */
700
- function autoFitFleetCardHeight(card) {
701
- const filename = card?.dataset?.filename || '';
702
- if (!filename) {
703
- return;
704
- }
705
- const chartState = getChartPanelState(filename);
706
- if (typeof chartState.height === 'number' && Number.isFinite(chartState.height)) {
707
- return;
708
- }
709
- const content = /** @type {HTMLElement | null} */ (card.querySelector('.artifact-content'));
710
- const iframe = /** @type {HTMLIFrameElement | null} */ (card.querySelector('.html-preview iframe'));
711
- if (!content || !iframe) {
712
- return;
713
- }
714
- let docHeight = 0;
715
- try {
716
- const doc = iframe.contentDocument;
717
- const body = doc?.body;
718
- const html = doc?.documentElement;
719
- docHeight = Math.max(
720
- body?.scrollHeight || 0,
721
- body?.offsetHeight || 0,
722
- html?.scrollHeight || 0,
723
- html?.offsetHeight || 0,
724
- html?.clientHeight || 0,
725
- );
726
- } catch {
727
- return;
728
- }
729
- if (!Number.isFinite(docHeight) || docHeight <= 0) {
730
- return;
731
- }
732
- const computed = window.getComputedStyle(content);
733
- const paddingTop = Number.parseFloat(computed.paddingTop || '0') || 0;
734
- const paddingBottom = Number.parseFloat(computed.paddingBottom || '0') || 0;
735
- const targetHeight = Math.round(Math.max(320, Math.min(2000, docHeight + paddingTop + paddingBottom)));
736
- content.style.height = `${targetHeight}px`;
737
- content.style.minHeight = `${targetHeight}px`;
738
- content.style.overflow = 'hidden';
739
- syncEmbeddedPreviewSize(content);
740
- setChartHeight(filename, targetHeight);
741
- }
742
-
743
- /**
744
- * Keep embedded HTML previews sized to the resized card and trigger a resize
745
- * event inside iframe widgets so responsive layouts can relayout.
746
- * @param {HTMLElement} content
747
- */
748
- function syncEmbeddedPreviewSize(content) {
749
- const preview = content.querySelector('.html-preview');
750
- if (preview instanceof HTMLElement) {
751
- preview.style.height = '100%';
752
- preview.style.minHeight = '0';
753
- }
754
- const iframe = content.querySelector('.html-preview iframe');
755
- if (iframe instanceof HTMLIFrameElement) {
756
- iframe.style.height = '100%';
757
- iframe.style.minHeight = '0';
758
- iframe.style.display = 'block';
759
- try {
760
- iframe.contentWindow?.dispatchEvent(new Event('resize'));
761
- } catch {
762
- // Cross-origin frame or transient load state; ignore.
763
- }
764
- }
765
- }
766
-
767
- /** @param {Element | null} card @param {string} filename */
768
- function attachResizeHandle(card, filename) {
769
- if (!card || !filename) return;
770
- const resizeHandle = card.querySelector('.resize-handle');
771
- const contentEl = card.querySelector('.artifact-content');
772
- if (!resizeHandle || !contentEl) return;
773
- const cardEl = /** @type {HTMLElement} */ (card);
774
- const content = /** @type {HTMLElement} */ (contentEl);
775
- const minH = 120;
776
- const maxH = 2000;
777
- resizeHandle.addEventListener('mousedown', (e) => {
778
- e.preventDefault();
779
- e.stopPropagation();
780
- const startY = e.clientY;
781
- const rect = content.getBoundingClientRect();
782
- const startHeight = rect.height;
783
- let didResize = false;
784
- let rafId = 0;
785
- const schedulePreviewResize = () => {
786
- if (rafId) {
787
- cancelAnimationFrame(rafId);
788
- }
789
- rafId = requestAnimationFrame(() => {
790
- rafId = 0;
791
- syncEmbeddedPreviewSize(content);
792
- });
793
- };
794
- const onMove = (moveEvent) => {
795
- const dy = moveEvent.clientY - startY;
796
- if (Math.abs(dy) > 2) {
797
- didResize = true;
798
- }
799
- let h = Math.round(startHeight + dy);
800
- h = Math.max(minH, Math.min(maxH, h));
801
- content.style.height = `${h}px`;
802
- content.style.minHeight = `${h}px`;
803
- content.style.overflow = 'hidden';
804
- schedulePreviewResize();
805
- };
806
- const onUp = () => {
807
- document.removeEventListener('mousemove', onMove);
808
- document.removeEventListener('mouseup', onUp);
809
- if (rafId) {
810
- cancelAnimationFrame(rafId);
811
- rafId = 0;
812
- }
813
- syncEmbeddedPreviewSize(content);
814
- if (didResize) {
815
- const finalHeight = content.getBoundingClientRect().height;
816
- setChartHeight(filename, Math.round(finalHeight));
817
- return;
818
- }
819
- const nextExpanded = !cardEl.classList.contains('expanded');
820
- cardEl.classList.toggle('expanded', nextExpanded);
821
- setChartExpanded(filename, nextExpanded);
822
- };
823
- document.addEventListener('mousemove', onMove);
824
- document.addEventListener('mouseup', onUp);
825
- });
826
- }
827
-
828
- function toggleSummarySection() {
829
- state.panelState.summaryHidden = !state.panelState.summaryHidden;
830
- applySummarySectionVisibility();
831
- savePanelState();
832
- }
833
-
834
- function applySummarySectionVisibility() {
835
- const section = document.getElementById('summary-section');
836
- const btn = document.getElementById('summary-toggle-btn');
837
- if (section) {
838
- section.classList.toggle('collapsed', state.panelState.summaryHidden === true);
839
- }
840
- if (btn) {
841
- btn.textContent = state.panelState.summaryHidden ? 'Show' : 'Hide';
842
- btn.title = state.panelState.summaryHidden ? 'Show summary section' : 'Hide summary section';
843
- btn.setAttribute('aria-label', state.panelState.summaryHidden ? 'Show summary' : 'Hide summary');
844
- }
845
- }
846
-
847
- function savePanelState() {
848
- panelApi.postMessage({
849
- type: 'savePanelState',
850
- panelId: PANEL_ID,
851
- state: state.panelState,
852
- });
853
- }
854
-
855
- /**
856
- * @param {string} filename
857
- * @param {'up' | 'down'} direction
858
- * @param {string[]} activeChartKeys
859
- */
860
- function moveChartInPanelState(filename, direction, activeChartKeys) {
861
- const targetKey = normalizeFilename(filename);
862
- if (!targetKey) {
863
- return;
864
- }
865
- getChartPanelState(filename);
866
- const orderedKeys = Object.entries(state.panelState.charts)
867
- .sort((a, b) => a[1].order - b[1].order || a[0].localeCompare(b[0]))
868
- .map(([key]) => key);
869
- const index = orderedKeys.indexOf(targetKey);
870
- if (index < 0) {
871
- return;
872
- }
873
- const normalizedActiveKeys = new Set(
874
- (activeChartKeys || []).map(key => normalizeFilename(key)).filter(Boolean),
875
- );
876
- normalizedActiveKeys.add(targetKey);
877
- const step = direction === 'up' ? -1 : 1;
878
- let swapIndex = index + step;
879
- while (swapIndex >= 0 && swapIndex < orderedKeys.length) {
880
- if (normalizedActiveKeys.has(orderedKeys[swapIndex])) {
881
- break;
882
- }
883
- swapIndex += step;
884
- }
885
- if (swapIndex < 0 || swapIndex >= orderedKeys.length || swapIndex === index) {
886
- return;
887
- }
888
- const [entry] = orderedKeys.splice(index, 1);
889
- orderedKeys.splice(swapIndex, 0, entry);
890
- orderedKeys.forEach((key, orderIndex) => {
891
- const chart = state.panelState.charts[key];
892
- chart.order = orderIndex + 1;
893
- });
894
- savePanelState();
895
- }
896
-
897
- /**
898
- * @param {string} filename
899
- */
900
- function toggleChartHiddenInPanelState(filename) {
901
- const chartState = getChartPanelState(filename);
902
- chartState.hidden = !chartState.hidden;
903
- savePanelState();
904
- }
905
-
906
- // -----------------------------------------------------------------------------
907
- // Summary Cards
908
- // -----------------------------------------------------------------------------
909
-
910
- /** Last rendered summary-cards payload, to skip no-op rebuilds of the summary strip. */
911
- let lastSummaryCardsSignature = '';
912
-
913
- /**
914
- * Render summary cards in the header
915
- * @param {Array<{label: string, value: string, icon: string}>} cards
916
- */
917
- function renderSummaryCards(cards) {
918
- const container = document.getElementById('summary-cards');
919
- if (!container) {
920
- return;
921
- }
922
- if (!cards || cards.length === 0) {
923
- lastSummaryCardsSignature = '';
924
- renderSummaryPlaceholders();
925
- return;
926
- }
927
-
928
- // The host posts summaryCards more than once per refresh; skip the rebuild (and its
929
- // visible flash) when the payload is identical to what is already rendered.
930
- const signature = JSON.stringify(cards);
931
- if (signature === lastSummaryCardsSignature && !container.classList.contains('loading')) {
932
- return;
933
- }
934
- lastSummaryCardsSignature = signature;
935
-
936
- container.style.display = 'grid';
937
- container.classList.remove('loading');
938
- container.innerHTML = cards.map(card => `
1
+ /*! Copyright (c) 2026 AgentHippo.ai. All rights reserved. Proprietary; see LICENSE. */
2
+ "use strict";const panelApi=window.panelApi,AGENTIDE_PANEL_JS_VERSION="2026.07.21";try{window.__AGENTIDE_PANEL_JS_VERSION__=AGENTIDE_PANEL_JS_VERSION}catch{}const PANEL_ID="default",VIEW_EXPORT_SNAPSHOT="viewExportSnapshot",REQUEST_VIEW_EXPORT_SNAPSHOT="requestViewExportSnapshot",LEGACY_REQUEST_LIVE_FLEET_SNAPSHOT="requestLiveFleetSnapshot",LEGACY_VIEW_EXPORT_SNAPSHOT="liveFleetSnapshot",ADD_VIEW_OPTION_VALUE="__add_view__";function createDefaultPanelState(){return{version:1,charts:{},summaryHidden:!1}}const state={artifacts:[],galleryIgnorePatterns:[],summaryCards:null,manifest:null,isRefreshing:!1,scope:"global",projectId:"",agentId:"",agentIds:[],views:[],activeViewId:"global",disabledCharts:new Set,panelState:createDefaultPanelState(),panelStateLoaded:!1,lastArtifactCatalogSignature:"",fleetAutoRefreshIntervalSeconds:30,fleetRefreshVersion:0,surfaceMode:"spotlight",needsGalleryRender:!1,pendingForceFreshHtml:!1,refreshingViewId:null};function flushPendingGalleryRender(){if(!state.needsGalleryRender)return;state.needsGalleryRender=!1;const e=state.pendingForceFreshHtml;state.pendingForceFreshHtml=!1,renderGallery(state.artifacts,{forceFreshHtml:e})}function requestGalleryRender(e){if(state.isRefreshing){state.needsGalleryRender=!0,e?.forceFreshHtml&&(state.pendingForceFreshHtml=!0);return}renderGallery(state.artifacts,{forceFreshHtml:!!e?.forceFreshHtml})}function resetRefreshTrackingForViewChange(){!state.isRefreshing&&!state.needsGalleryRender&&!state.pendingForceFreshHtml||(state.isRefreshing=!1,state.refreshingViewId=null,state.needsGalleryRender=!1,state.pendingForceFreshHtml=!1,setRefreshButtonState(!1),setPerChartRefreshing(!1))}function applySurfaceMode(e){const t=e==="team";state.surfaceMode=t?"team":"spotlight",document.body.classList.toggle("spotlight-surface-team",t),applyTeamViewSelectVisibility()}let fleetAutoRefreshTimerId=null;function isFleetWidgetAtTop(){const e=document.querySelector(".fleet-dashboard-full.artifact-card");if(!e)return!0;const t=e.querySelector(".artifact-content");if(t&&t.scrollTop>4)return!1;const n=e.querySelector(".html-preview iframe");if(!n)return!0;try{const a=n.contentDocument,r=a?.body?.scrollTop??0,s=a?.documentElement?.scrollTop??0,o=n.contentWindow?.scrollY??0;return r<=4&&s<=4&&o<=4}catch{return!0}}function getActiveViewConfig(){return state.views.find(e=>e.id===state.activeViewId)}function activeViewUsesFleetDashboardLayout(){return getActiveViewConfig()?.layout==="fleet-dashboard"}function activeViewPreservesFleetIframe(){return getActiveViewConfig()?.preserveFleetIframe===!0}function activeViewIsLiveFleetSnapshot(){return activeViewPreservesFleetIframe()?!0:/^live-fleet-\d+$/.test(String(state.activeViewId||""))}function applyFleetAutoRefresh(){fleetAutoRefreshTimerId!==null&&(clearInterval(fleetAutoRefreshTimerId),fleetAutoRefreshTimerId=null);const e=state.fleetAutoRefreshIntervalSeconds;if((state.activeViewId==="fleet"||activeViewPreservesFleetIframe())&&typeof e=="number"&&e>0){const n=e*1e3;fleetAutoRefreshTimerId=setInterval(()=>{if(isFleetWidgetAtTop()){if(activeViewPreservesFleetIframe()){panelApi.postMessage({type:REQUEST_VIEW_EXPORT_SNAPSHOT});return}panelApi.postMessage({type:"refreshView"})}},n)}}function applyTeamViewSelectVisibility(){const e=document.getElementById("view-select");e&&(state.surfaceMode==="team"?state.views.length>1?e.style.setProperty("display","inline-block","important"):e.style.setProperty("display","none","important"):e.style.removeProperty("display"))}function applyViewOptions(){const e=document.getElementById("view-select");if(!e)return;const t=state.activeViewId||e.value||"";if(e.innerHTML="",state.surfaceMode!=="team"){const a=document.createElement("option");a.value=ADD_VIEW_OPTION_VALUE,a.textContent="+ Add view\u2026",e.appendChild(a)}for(const a of state.views){const r=document.createElement("option");r.value=a.id,r.textContent=a.name,e.appendChild(r)}const n=state.views.some(a=>a.id===state.activeViewId)?state.activeViewId:t;n&&state.views.some(a=>a.id===n)&&(e.value=n,state.activeViewId=n),applyTeamViewSelectVisibility(),applyShareButtonState()}const SHARE_ICON_HTML='<span class="btn-icon" aria-hidden="true">&#8593;</span>',INSTALL_ICON_HTML='<span class="btn-icon" aria-hidden="true">&#8595;</span>';function onInstallViewClick(){panelApi.postMessage({type:"installView"})}function onShareViewClick(){const e=getActiveView();panelApi.postMessage({type:"shareView",viewId:e?.id??"",bundle:"full"})}function onShareScriptsOnlyClick(){const e=getActiveView();panelApi.postMessage({type:"shareView",viewId:e?.id??"",bundle:"scripts-only"})}function ensureShareToolbarButton(){let e=document.getElementById("share-view-btn");if(e){const n=document.getElementById("refresh-charts-btn"),a=e.querySelector(".btn-icon");a&&a.textContent!=="\u2191"&&(a.outerHTML=SHARE_ICON_HTML),n&&e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING&&n.insertAdjacentElement("afterend",e)}else{const n=document.getElementById("refresh-charts-btn");if(!n?.parentElement)return;let a=n.closest(".toolbar-icon-group");a||(a=document.createElement("div"),a.className="toolbar-icon-group",n.parentElement.insertBefore(a,n),a.appendChild(n)),e=document.createElement("button"),e.id="share-view-btn",e.className="toolbar-btn icon-only",e.title="Publish current view to store (full bundle)",e.setAttribute("aria-label",e.title),e.innerHTML=SHARE_ICON_HTML,a.appendChild(e)}const t=document.getElementById("share-view-btn");t&&!t.dataset.boundShareClick&&(t.dataset.boundShareClick="1",t.addEventListener("click",onShareViewClick)),applyShareButtonState(),ensureInstallToolbarButton()}function ensureInstallToolbarButton(){let e=document.getElementById("install-view-btn");if(e){const n=document.getElementById("share-view-btn"),a=e.querySelector(".btn-icon");a&&a.textContent!=="\u2193"&&(a.outerHTML=INSTALL_ICON_HTML),n&&e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING&&n.insertAdjacentElement("afterend",e)}else{const n=document.getElementById("share-view-btn");if(!n?.parentElement)return;let a=n.closest(".toolbar-icon-group");a||(a=document.createElement("div"),a.className="toolbar-icon-group",n.parentElement.insertBefore(a,n),a.appendChild(n)),e=document.createElement("button"),e.id="install-view-btn",e.className="toolbar-btn icon-only",e.title="Install Spotlight view from store",e.setAttribute("aria-label",e.title),e.innerHTML=INSTALL_ICON_HTML,n.insertAdjacentElement("afterend",e)}const t=document.getElementById("install-view-btn");t&&!t.dataset.boundInstallClick&&(t.dataset.boundInstallClick="1",t.addEventListener("click",onInstallViewClick))}function applyShareButtonState(){const e=document.getElementById("share-view-btn");if(!e)return;const t=getActiveView(),n=!!(t&&!t.system);e.classList.toggle("share-unavailable",!n),e.title=n?"Publish current view to store (full bundle)":"Built-in views cannot be shared (duplicate or create a custom view)",e.setAttribute("aria-label",e.title);const a=document.getElementById("share-scripts-only-menu-item");a&&(a.disabled=!1,a.classList.toggle("share-unavailable",!n),a.title=n?"Publish widget scripts only (no charts or exports)":"Built-in views cannot be shared (duplicate or create a custom view)")}function getActiveView(){return state.views.find(e=>e.id===state.activeViewId)}function switchToView(e){if(!e||e===state.activeViewId)return;state.activeViewId=e,resetRefreshTrackingForViewChange(),state.panelStateLoaded=!1,state.panelState=createDefaultPanelState();const t=document.getElementById("view-select");t&&state.views.some(n=>n.id===e)&&(t.value=e),applyFleetAutoRefresh(),applyShareButtonState(),panelApi.postMessage({type:"setActiveView",viewId:e}),panelApi.postMessage({type:"requestPanelState",panelId:PANEL_ID})}function showNewViewModal(e){const t=document.getElementById("new-view-modal"),n=document.getElementById("new-view-name-input"),a=document.getElementById("new-view-modal-ok"),r=document.getElementById("new-view-modal-cancel");if(!t||!n||!a||!r)return;typeof e=="function"&&e(),n.value="",t.style.display="flex",t.setAttribute("aria-hidden","false"),n.focus();function s(){t.style.display="none",t.setAttribute("aria-hidden","true"),t.removeEventListener("click",f),document.removeEventListener("keydown",l)}function o(){const c=n.value.trim();s(),c&&panelApi.postMessage({type:"createView",name:c})}function f(c){c.target===t&&s()}function l(c){c.key==="Escape"?(s(),c.preventDefault()):c.key==="Enter"&&(o(),c.preventDefault())}t.addEventListener("click",f),document.addEventListener("keydown",l),a.onclick=()=>o(),r.onclick=()=>s()}function promptCreateView(){showNewViewModal()}function initViews(){const e=document.getElementById("view-select");e?.addEventListener("change",()=>{const t=e.value||"";if(t){if(t===ADD_VIEW_OPTION_VALUE){showNewViewModal(()=>{e.value=state.activeViewId||""});return}t!==state.activeViewId&&switchToView(t)}})}function refreshCurrentView(){const e=document.getElementById("refresh-charts-btn");e&&!e.classList.contains("refreshing")&&(setRefreshButtonState(!0),panelApi.postMessage({type:"refreshView"}))}function formatTimeAgo(e){const t=Math.floor((Date.now()-e.getTime())/1e3);return t<60?"just now":t<3600?`${Math.floor(t/60)} min ago`:t<86400?`${Math.floor(t/3600)}h ago`:`${Math.floor(t/86400)}d ago`}function formatSize(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}function escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function renderMarkdown(e){return e.replace(/```(\w*)\n([\s\S]*?)```/g,'<pre><code class="language-$1">$2</code></pre>').replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>").replace(/\*(.+?)\*/g,"<em>$1</em>").replace(/^### (.+)$/gm,"<h3>$1</h3>").replace(/^## (.+)$/gm,"<h2>$1</h2>").replace(/^# (.+)$/gm,"<h1>$1</h1>").replace(/\n/g,"<br>")}function normalizeFilename(e){return String(e||"").trim().toLowerCase()}function normalizePanelState(e){const t=e&&typeof e=="object"?e:{},n=t.charts&&typeof t.charts=="object"?t.charts:{},a={};let r=1;for(const[o,f]of Object.entries(n)){const l=normalizeFilename(o);if(!l)continue;const c=f&&typeof f=="object"?f:{},p=Number(c.order),v=Number.isFinite(p)&&p>0?p:r,L=c.hidden===!0,S=Number(c.height),x=c.expanded===!0;a[l]={order:v,hidden:L,height:Number.isFinite(S)&&S>=120?S:void 0,expanded:x},r=Math.max(r,v+1)}const s=t.summaryHidden===!0;return{version:1,charts:a,summaryHidden:s}}function getNextChartOrder(){const e=Object.values(state.panelState.charts);return e.length===0?1:e.reduce((t,n)=>Math.max(t,n.order),0)+1}function ensurePanelStateForArtifacts(e){let t=!1,n=getNextChartOrder();for(const a of e){if(a.type!=="chart"&&a.type!=="image")continue;const r=normalizeFilename(String(a.filename||""));r&&(state.panelState.charts[r]||(state.panelState.charts[r]={order:n++,hidden:!1,height:void 0,expanded:!1},t=!0))}return t}function getChartPanelState(e){const t=normalizeFilename(e);return state.panelState.charts[t]||(state.panelState.charts[t]={order:getNextChartOrder(),hidden:!1,height:void 0,expanded:!1}),state.panelState.charts[t]}function setChartHeight(e,t){const n=getChartPanelState(e);n.height=t>=120&&t<=2e3?t:void 0,savePanelState()}function setChartExpanded(e,t){const n=getChartPanelState(e);n.expanded=t===!0,savePanelState()}function autoFitFleetCardHeight(e){const t=e?.dataset?.filename||"";if(!t)return;const n=getChartPanelState(t);if(typeof n.height=="number"&&Number.isFinite(n.height))return;const a=e.querySelector(".artifact-content"),r=e.querySelector(".html-preview iframe");if(!a||!r)return;let s=0;try{const p=r.contentDocument,v=p?.body,L=p?.documentElement;s=Math.max(v?.scrollHeight||0,v?.offsetHeight||0,L?.scrollHeight||0,L?.offsetHeight||0,L?.clientHeight||0)}catch{return}if(!Number.isFinite(s)||s<=0)return;const o=window.getComputedStyle(a),f=Number.parseFloat(o.paddingTop||"0")||0,l=Number.parseFloat(o.paddingBottom||"0")||0,c=Math.round(Math.max(320,Math.min(2e3,s+f+l)));a.style.height=`${c}px`,a.style.minHeight=`${c}px`,a.style.overflow="hidden",syncEmbeddedPreviewSize(a),setChartHeight(t,c)}function syncEmbeddedPreviewSize(e){const t=e.querySelector(".html-preview");t instanceof HTMLElement&&(t.style.height="100%",t.style.minHeight="0");const n=e.querySelector(".html-preview iframe");if(n instanceof HTMLIFrameElement){n.style.height="100%",n.style.minHeight="0",n.style.display="block";try{n.contentWindow?.dispatchEvent(new Event("resize"))}catch{}}}function attachResizeHandle(e,t){if(!e||!t)return;const n=e.querySelector(".resize-handle"),a=e.querySelector(".artifact-content");if(!n||!a)return;const r=e,s=a,o=120,f=2e3;n.addEventListener("mousedown",l=>{l.preventDefault(),l.stopPropagation();const c=l.clientY,v=s.getBoundingClientRect().height;let L=!1,S=0;const x=()=>{S&&cancelAnimationFrame(S),S=requestAnimationFrame(()=>{S=0,syncEmbeddedPreviewSize(s)})},k=M=>{const T=M.clientY-c;Math.abs(T)>2&&(L=!0);let i=Math.round(v+T);i=Math.max(o,Math.min(f,i)),s.style.height=`${i}px`,s.style.minHeight=`${i}px`,s.style.overflow="hidden",x()},H=()=>{if(document.removeEventListener("mousemove",k),document.removeEventListener("mouseup",H),S&&(cancelAnimationFrame(S),S=0),syncEmbeddedPreviewSize(s),L){const T=s.getBoundingClientRect().height;setChartHeight(t,Math.round(T));return}const M=!r.classList.contains("expanded");r.classList.toggle("expanded",M),setChartExpanded(t,M)};document.addEventListener("mousemove",k),document.addEventListener("mouseup",H)})}function toggleSummarySection(){state.panelState.summaryHidden=!state.panelState.summaryHidden,applySummarySectionVisibility(),savePanelState()}function applySummarySectionVisibility(){const e=document.getElementById("summary-section"),t=document.getElementById("summary-toggle-btn");e&&e.classList.toggle("collapsed",state.panelState.summaryHidden===!0),t&&(t.textContent=state.panelState.summaryHidden?"Show":"Hide",t.title=state.panelState.summaryHidden?"Show summary section":"Hide summary section",t.setAttribute("aria-label",state.panelState.summaryHidden?"Show summary":"Hide summary"))}function savePanelState(){panelApi.postMessage({type:"savePanelState",panelId:PANEL_ID,state:state.panelState})}function moveChartInPanelState(e,t,n){const a=normalizeFilename(e);if(!a)return;getChartPanelState(e);const r=Object.entries(state.panelState.charts).sort((p,v)=>p[1].order-v[1].order||p[0].localeCompare(v[0])).map(([p])=>p),s=r.indexOf(a);if(s<0)return;const o=new Set((n||[]).map(p=>normalizeFilename(p)).filter(Boolean));o.add(a);const f=t==="up"?-1:1;let l=s+f;for(;l>=0&&l<r.length&&!o.has(r[l]);)l+=f;if(l<0||l>=r.length||l===s)return;const[c]=r.splice(s,1);r.splice(l,0,c),r.forEach((p,v)=>{const L=state.panelState.charts[p];L.order=v+1}),savePanelState()}function toggleChartHiddenInPanelState(e){const t=getChartPanelState(e);t.hidden=!t.hidden,savePanelState()}let lastSummaryCardsSignature="";function renderSummaryCards(e){const t=document.getElementById("summary-cards");if(!t)return;if(!e||e.length===0){lastSummaryCardsSignature="",renderSummaryPlaceholders();return}const n=JSON.stringify(e);n===lastSummaryCardsSignature&&!t.classList.contains("loading")||(lastSummaryCardsSignature=n,t.style.display="grid",t.classList.remove("loading"),t.innerHTML=e.map(a=>`
939
3
  <div class="metric-card">
940
- <div class="metric-icon">${card.icon}</div>
941
- <div class="metric-value">${escapeHtml(card.value)}</div>
942
- <div class="metric-label">${escapeHtml(card.label)}</div>
4
+ <div class="metric-icon">${a.icon}</div>
5
+ <div class="metric-value">${escapeHtml(a.value)}</div>
6
+ <div class="metric-label">${escapeHtml(a.label)}</div>
943
7
  </div>
944
- `).join('');
945
- }
946
-
947
- /**
948
- * Render summary placeholders while data is loading.
949
- */
950
- function renderSummaryPlaceholders() {
951
- const container = document.getElementById('summary-cards');
952
- if (!container) {
953
- return;
954
- }
955
-
956
- container.style.display = 'grid';
957
- container.classList.add('loading');
958
- container.innerHTML = Array.from({ length: 4 }).map(() => `
8
+ `).join(""))}function renderSummaryPlaceholders(){const e=document.getElementById("summary-cards");e&&(e.style.display="grid",e.classList.add("loading"),e.innerHTML=Array.from({length:4}).map(()=>`
959
9
  <div class="metric-card skeleton">
960
10
  <div class="skeleton-line skeleton-icon"></div>
961
11
  <div class="skeleton-line skeleton-value"></div>
962
12
  <div class="skeleton-line skeleton-label"></div>
963
13
  </div>
964
- `).join('');
965
- }
966
-
967
- // -----------------------------------------------------------------------------
968
- // Gallery Rendering
969
- // -----------------------------------------------------------------------------
970
-
971
- /**
972
- * Show a specific gallery state
973
- * @param {'loading' | 'empty' | 'generating' | 'failed' | 'content'} stateName
974
- */
975
- function showGalleryState(stateName) {
976
- const states = ['loading-state', 'empty-state', 'generating-state', 'failed-state', 'gallery-content'];
977
- for (const id of states) {
978
- const el = document.getElementById(id);
979
- if (el) {
980
- el.style.display = id === `${stateName}-state` || id === `${stateName}` ? 'flex' : 'none';
981
- }
982
- }
983
-
984
- // gallery-content needs 'block' not 'flex'
985
- const content = document.getElementById('gallery-content');
986
- if (content) {
987
- content.style.display = stateName === 'content' ? 'block' : 'none';
988
- }
989
- }
990
-
991
- /**
992
- * Render the artifact gallery
993
- * @param {Array<object>} artifacts
994
- * @param {{ forceFreshHtml?: boolean }} [options]
995
- */
996
- function renderGallery(artifacts, options) {
997
- const forceFreshHtml = options?.forceFreshHtml === true;
998
- const container = document.getElementById('gallery-content');
999
- if (!container) {
1000
- return;
1001
- }
1002
- // Preserve existing HTML iframes across incremental refreshes when the
1003
- // artifact content version (modifiedAt) did not change.
1004
- /** @type {Map<string, HTMLIFrameElement>} */
1005
- const reusableHtmlPreviews = new Map();
1006
- container.querySelectorAll('.artifact-card').forEach((cardEl) => {
1007
- const card = /** @type {HTMLElement} */ (cardEl);
1008
- const path = card.dataset.path || '';
1009
- const modifiedAt = card.dataset.modifiedAt || '';
1010
- if (!path || !modifiedAt) {
1011
- return;
1012
- }
1013
- const iframe = /** @type {HTMLIFrameElement | null} */ (card.querySelector('.html-preview iframe'));
1014
- if (!iframe) {
1015
- return;
1016
- }
1017
- reusableHtmlPreviews.set(`${path}::${modifiedAt}`, iframe);
1018
- });
1019
-
1020
- const layoutChanged = ensurePanelStateForArtifacts(artifacts);
1021
- if (layoutChanged && state.panelStateLoaded) {
1022
- savePanelState();
1023
- }
1024
-
1025
- const visibleArtifacts = artifacts.filter((artifact) => {
1026
- const filename = String(artifact.filename || '').trim().toLowerCase();
1027
- // Hide artifacts matching galleryIgnorePatterns (suffix or exact name); list is from disabled-charts.yaml
1028
- const ignored = state.galleryIgnorePatterns.some((p) => {
1029
- const pattern = String(p).trim().toLowerCase();
1030
- return pattern && (filename.endsWith(pattern) || filename === pattern);
1031
- });
1032
- if (ignored) {
1033
- return false;
1034
- }
1035
- // Live Fleet iframe views: export JSON is pushed via viewExportSnapshot — skip gallery cards (Open/Delete).
1036
- if (activeViewIsLiveFleetSnapshot() && artifact.type !== 'chart' && artifact.type !== 'image') {
1037
- return false;
1038
- }
1039
- if (artifact.type !== 'chart' && artifact.type !== 'image') {
1040
- return true;
1041
- }
1042
- return !state.disabledCharts.has(filename);
1043
- });
1044
-
1045
- const chartArtifacts = visibleArtifacts
1046
- .filter((artifact) => artifact.type === 'chart' || artifact.type === 'image')
1047
- .map((artifact) => ({
1048
- ...artifact,
1049
- _panel: getChartPanelState(String(artifact.filename || '')),
1050
- }))
1051
- .sort((a, b) => {
1052
- if (a._panel.hidden !== b._panel.hidden) {
1053
- return a._panel.hidden ? 1 : -1;
1054
- }
1055
- if (a._panel.order !== b._panel.order) {
1056
- return a._panel.order - b._panel.order;
1057
- }
1058
- return new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime();
1059
- });
1060
- const nonChartArtifacts = visibleArtifacts
1061
- .filter((artifact) => artifact.type !== 'chart')
1062
- .sort((a, b) => new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime());
1063
- const orderedArtifacts = [...chartArtifacts, ...nonChartArtifacts];
1064
-
1065
- if (orderedArtifacts.length === 0) {
1066
- showGalleryState('empty');
1067
- return;
1068
- }
1069
-
1070
- // Export JSON alongside the chart is normal for live fleet views — do not fall back to gallery layout.
1071
- const nonChartNonExportArtifacts = nonChartArtifacts.filter((artifact) => artifact.type !== 'export');
1072
- const fleetHtmlCharts = chartArtifacts.filter((artifact) => artifact.format === 'html');
1073
- const isFleetSingleHtml =
1074
- (fleetHtmlCharts.length >= 1 && activeViewIsLiveFleetSnapshot())
1075
- || (activeViewUsesFleetDashboardLayout() &&
1076
- chartArtifacts.length === 1 &&
1077
- chartArtifacts[0].format === 'html' &&
1078
- nonChartNonExportArtifacts.length === 0);
1079
- if (isFleetSingleHtml) {
1080
- const artifact = fleetHtmlCharts[0];
1081
- const path = artifact.localPath || '';
1082
- const modifiedAt = String(artifact.modifiedAt || '');
1083
- const fleetFilename = String(artifact.filename || '').trim() || '__fleet__';
1084
- const chartState = getChartPanelState(fleetFilename);
1085
- const fleetExpandedClass = chartState.expanded ? ' expanded' : '';
1086
- const fleetHeightStyle = chartState.height
1087
- ? `style="height: ${chartState.height}px; min-height: ${chartState.height}px; overflow: auto;"`
1088
- : '';
1089
- const existingFleetCard = /** @type {HTMLElement | null} */ (
1090
- container.querySelector('.fleet-dashboard-full.artifact-card')
1091
- );
1092
- // preserveFleetIframe views: keep the iframe mounted; data updates arrive via viewExportSnapshot postMessage.
1093
- let fleetIframeHasSnapshotShell = false;
1094
- if (activeViewPreservesFleetIframe() && existingFleetCard) {
1095
- const fleetIframe = existingFleetCard.querySelector('.html-preview iframe');
1096
- if (fleetIframe instanceof HTMLIFrameElement) {
1097
- try {
1098
- const doc = fleetIframe.contentDocument;
1099
- fleetIframeHasSnapshotShell =
1100
- doc?.body?.dataset?.fleetSnapshotShell === '1'
1101
- || doc?.documentElement?.hasAttribute('data-live-fleet-shell');
1102
- } catch {
1103
- fleetIframeHasSnapshotShell = false;
1104
- }
1105
- }
1106
- }
1107
- if (
1108
- activeViewPreservesFleetIframe()
1109
- && existingFleetCard
1110
- && existingFleetCard.dataset.path === path
1111
- && existingFleetCard.querySelector('.html-preview iframe')
1112
- && fleetIframeHasSnapshotShell
1113
- && !forceFreshHtml
1114
- ) {
1115
- showGalleryState('content');
1116
- existingFleetCard.dataset.modifiedAt = modifiedAt;
1117
- attachResizeHandle(existingFleetCard, fleetFilename);
1118
- return;
1119
- }
1120
- const reusable = forceFreshHtml ? undefined : reusableHtmlPreviews.get(`${path}::${modifiedAt}`);
1121
- showGalleryState('content');
1122
- container.innerHTML = `
1123
- <div class="fleet-dashboard-full artifact-card${fleetExpandedClass}" data-path="${escapeHtml(path)}" data-filename="${escapeHtml(fleetFilename)}" data-modified-at="${escapeHtml(modifiedAt)}">
1124
- <div class="artifact-content" ${fleetHeightStyle}>
1125
- <div class="html-preview">${reusable ? '' : '<div class="html-preview-loading">Loading Fleet Operations...</div>'}</div>
14
+ `).join(""))}function showGalleryState(e){const t=["loading-state","empty-state","generating-state","failed-state","gallery-content"];for(const a of t){const r=document.getElementById(a);r&&(r.style.display=a===`${e}-state`||a===`${e}`?"flex":"none")}const n=document.getElementById("gallery-content");n&&(n.style.display=e==="content"?"block":"none")}function renderGallery(e,t){const n=t?.forceFreshHtml===!0,a=document.getElementById("gallery-content");if(!a)return;const r=new Map;a.querySelectorAll(".artifact-card").forEach(i=>{const d=i,b=d.dataset.path||"",g=d.dataset.modifiedAt||"";if(!b||!g)return;const m=d.querySelector(".html-preview iframe");m&&r.set(`${b}::${g}`,m)}),ensurePanelStateForArtifacts(e)&&state.panelStateLoaded&&savePanelState();const o=e.filter(i=>{const d=String(i.filename||"").trim().toLowerCase();return state.galleryIgnorePatterns.some(g=>{const m=String(g).trim().toLowerCase();return m&&(d.endsWith(m)||d===m)})||activeViewIsLiveFleetSnapshot()&&i.type!=="chart"&&i.type!=="image"?!1:i.type!=="chart"&&i.type!=="image"?!0:!state.disabledCharts.has(d)}),f=o.filter(i=>i.type==="chart"||i.type==="image").map(i=>({...i,_panel:getChartPanelState(String(i.filename||""))})).sort((i,d)=>i._panel.hidden!==d._panel.hidden?i._panel.hidden?1:-1:i._panel.order!==d._panel.order?i._panel.order-d._panel.order:new Date(d.modifiedAt).getTime()-new Date(i.modifiedAt).getTime()),l=o.filter(i=>i.type!=="chart").sort((i,d)=>new Date(d.modifiedAt).getTime()-new Date(i.modifiedAt).getTime()),c=[...f,...l];if(c.length===0){showGalleryState("empty");return}const p=l.filter(i=>i.type!=="export"),v=f.filter(i=>i.format==="html");if(v.length>=1&&activeViewIsLiveFleetSnapshot()||activeViewUsesFleetDashboardLayout()&&f.length===1&&f[0].format==="html"&&p.length===0){const i=v[0],d=i.localPath||"",b=String(i.modifiedAt||""),g=String(i.filename||"").trim()||"__fleet__",m=getChartPanelState(g),I=m.expanded?" expanded":"",E=m.height?`style="height: ${m.height}px; min-height: ${m.height}px; overflow: auto;"`:"",u=a.querySelector(".fleet-dashboard-full.artifact-card");let A=!1;if(activeViewPreservesFleetIframe()&&u){const h=u.querySelector(".html-preview iframe");if(h instanceof HTMLIFrameElement)try{const C=h.contentDocument;A=C?.body?.dataset?.fleetSnapshotShell==="1"||C?.documentElement?.hasAttribute("data-live-fleet-shell")}catch{A=!1}}if(activeViewPreservesFleetIframe()&&u&&u.dataset.path===d&&u.querySelector(".html-preview iframe")&&A&&!n){showGalleryState("content"),u.dataset.modifiedAt=b,attachResizeHandle(u,g);return}const w=n?void 0:r.get(`${d}::${b}`);if(showGalleryState("content"),a.innerHTML=`
15
+ <div class="fleet-dashboard-full artifact-card${I}" data-path="${escapeHtml(d)}" data-filename="${escapeHtml(g)}" data-modified-at="${escapeHtml(b)}">
16
+ <div class="artifact-content" ${E}>
17
+ <div class="html-preview">${w?"":'<div class="html-preview-loading">Loading Fleet Operations...</div>'}</div>
1126
18
  </div>
1127
- <div class="resize-handle" data-filename="${escapeHtml(fleetFilename)}" title="Drag to resize height" role="separator"></div>
19
+ <div class="resize-handle" data-filename="${escapeHtml(g)}" title="Drag to resize height" role="separator"></div>
1128
20
  </div>
1129
- `;
1130
- if (reusable) {
1131
- const host = container.querySelector('.html-preview');
1132
- if (host) {
1133
- host.classList.add('ready');
1134
- host.appendChild(reusable);
1135
- }
1136
- } else {
1137
- requestHtmlPreviewContents([artifact]);
1138
- }
1139
- const fleetCard = container.querySelector('.artifact-card');
1140
- attachResizeHandle(fleetCard, fleetFilename);
1141
- if (fleetCard instanceof HTMLElement) {
1142
- fleetCard.addEventListener('click', (event) => {
1143
- const target = /** @type {HTMLElement | null} */ (event.target instanceof HTMLElement ? event.target : null);
1144
- if (target?.closest('button,.resize-handle')) {
1145
- return;
1146
- }
1147
- const nextExpanded = !fleetCard.classList.contains('expanded');
1148
- fleetCard.classList.toggle('expanded', nextExpanded);
1149
- setChartExpanded(fleetFilename, nextExpanded);
1150
- });
1151
- }
1152
- return;
1153
- }
1154
-
1155
- showGalleryState('content');
1156
-
1157
- const buildCardHtml = (artifact) => {
1158
- const timeAgo = formatTimeAgo(new Date(artifact.modifiedAt));
1159
- const sizeFormatted = formatSize(artifact.size);
1160
- const isChart = artifact.type === 'chart';
1161
- const isImage = artifact.type === 'image';
1162
- const isVisual = isChart || isImage;
1163
- const chartState = isVisual ? artifact._panel : undefined;
1164
- const isHidden = chartState?.hidden === true;
1165
- const isExpanded = chartState?.expanded === true;
1166
- const order = Number.isFinite(chartState?.order) ? chartState.order : undefined;
1167
-
1168
- // Render based on format
1169
- let content = '';
1170
- switch (artifact.format) {
1171
- case 'png':
1172
- case 'svg':
1173
- case 'jpg':
1174
- content = `<img src="${artifact.webviewUri}" alt="${escapeHtml(artifact.filename)}" loading="lazy" />`;
1175
- break;
1176
- case 'html':
1177
- content = `
1178
- <div class="html-preview" data-path="${escapeHtml(artifact.localPath)}">
21
+ `,w){const h=a.querySelector(".html-preview");h&&(h.classList.add("ready"),h.appendChild(w))}else requestHtmlPreviewContents([i]);const y=a.querySelector(".artifact-card");attachResizeHandle(y,g),y instanceof HTMLElement&&y.addEventListener("click",h=>{if((h.target instanceof HTMLElement?h.target:null)?.closest("button,.resize-handle"))return;const V=!y.classList.contains("expanded");y.classList.toggle("expanded",V),setChartExpanded(g,V)});return}showGalleryState("content");const S=i=>{const d=formatTimeAgo(new Date(i.modifiedAt)),b=formatSize(i.size),g=i.type==="chart",m=i.type==="image",I=g||m,E=I?i._panel:void 0,u=E?.hidden===!0,A=E?.expanded===!0,w=Number.isFinite(E?.order)?E.order:void 0;let y="";switch(i.format){case"png":case"svg":case"jpg":y=`<img src="${i.webviewUri}" alt="${escapeHtml(i.filename)}" loading="lazy" />`;break;case"html":y=`
22
+ <div class="html-preview" data-path="${escapeHtml(i.localPath)}">
1179
23
  <div class="html-preview-loading">Loading preview...</div>
1180
24
  </div>
1181
- `;
1182
- break;
1183
- case 'md':
1184
- content = `<div class="markdown-preview" data-path="${escapeHtml(artifact.localPath)}">Loading...</div>`;
1185
- break;
1186
- case 'csv':
1187
- case 'json':
1188
- content = `<div class="data-preview"><code>${escapeHtml(artifact.filename)}</code></div>`;
1189
- break;
1190
- default:
1191
- content = `<div class="data-preview"><code>${escapeHtml(artifact.filename)}</code></div>`;
1192
- }
1193
-
1194
- // Find manifest entry for this artifact
1195
- const manifestEntry = state.manifest?.artifacts?.find(e => e.filename === artifact.filename);
1196
- const title = manifestEntry?.title || artifact.filename;
1197
- const generatorBadge = artifact.generator
1198
- ? `<span class="generator-badge">${escapeHtml(artifact.generator)}</span>`
1199
- : '';
1200
- const sourceBadge = manifestEntry?.source === 'default'
1201
- ? '<span class="source-badge">Auto</span>'
1202
- : '';
1203
- const hiddenBadge = isVisual && isHidden
1204
- ? '<span class="layout-badge disabled">Hidden</span>'
1205
- : '';
1206
- const orderBadge = isVisual && Number.isFinite(order)
1207
- ? `<span class="layout-badge">#${order}</span>`
1208
- : '';
1209
- const layoutActions = isVisual
1210
- ? `
1211
- <button class="action-btn move-up-btn" title="Move up">↑</button>
1212
- <button class="action-btn move-down-btn" title="Move down">↓</button>
1213
- <button class="action-btn toggle-enabled-btn" title="${isHidden ? 'Show chart' : 'Hide chart'}">
1214
- ${isHidden ? 'Show' : 'Hide'}
25
+ `;break;case"md":y=`<div class="markdown-preview" data-path="${escapeHtml(i.localPath)}">Loading...</div>`;break;case"csv":case"json":y=`<div class="data-preview"><code>${escapeHtml(i.filename)}</code></div>`;break;default:y=`<div class="data-preview"><code>${escapeHtml(i.filename)}</code></div>`}const h=state.manifest?.artifacts?.find(_=>_.filename===i.filename),C=h?.title||i.filename,V=i.generator?`<span class="generator-badge">${escapeHtml(i.generator)}</span>`:"",F=h?.source==="default"?'<span class="source-badge">Auto</span>':"",B=I&&u?'<span class="layout-badge disabled">Hidden</span>':"",$=I&&Number.isFinite(w)?`<span class="layout-badge">#${w}</span>`:"",q=I?`
26
+ <button class="action-btn move-up-btn" title="Move up">\u2191</button>
27
+ <button class="action-btn move-down-btn" title="Move down">\u2193</button>
28
+ <button class="action-btn toggle-enabled-btn" title="${u?"Show chart":"Hide chart"}">
29
+ ${u?"Show":"Hide"}
1215
30
  </button>
1216
- `
1217
- : '';
1218
- const contentHeightStyle = isVisual && chartState?.height
1219
- ? `style="height: ${chartState.height}px; min-height: ${chartState.height}px; overflow: auto;"`
1220
- : '';
1221
- const resizeHandle = isVisual
1222
- ? `<div class="resize-handle" data-filename="${escapeHtml(artifact.filename)}" title="Drag to resize height" role="separator"></div>`
1223
- : '';
1224
-
1225
- return `
1226
- <div class="artifact-card ${isVisual && isHidden ? 'layout-disabled' : ''}${isExpanded ? ' expanded' : ''}" data-path="${escapeHtml(artifact.localPath)}" data-type="${artifact.type}" data-filename="${escapeHtml(artifact.filename)}" data-modified-at="${escapeHtml(String(artifact.modifiedAt || ''))}">
1227
- <div class="artifact-content" ${contentHeightStyle}>
1228
- ${content}
31
+ `:"",P=I&&E?.height?`style="height: ${E.height}px; min-height: ${E.height}px; overflow: auto;"`:"",R=I?`<div class="resize-handle" data-filename="${escapeHtml(i.filename)}" title="Drag to resize height" role="separator"></div>`:"";return`
32
+ <div class="artifact-card ${I&&u?"layout-disabled":""}${A?" expanded":""}" data-path="${escapeHtml(i.localPath)}" data-type="${i.type}" data-filename="${escapeHtml(i.filename)}" data-modified-at="${escapeHtml(String(i.modifiedAt||""))}">
33
+ <div class="artifact-content" ${P}>
34
+ ${y}
1229
35
  </div>
1230
36
  <div class="artifact-meta">
1231
- <span class="artifact-name" title="${escapeHtml(artifact.filename)}">${escapeHtml(title)}</span>
1232
- ${sourceBadge}
1233
- ${hiddenBadge}
1234
- ${orderBadge}
1235
- ${generatorBadge}
1236
- <span class="artifact-time">${timeAgo}</span>
1237
- <span class="artifact-size">${sizeFormatted}</span>
37
+ <span class="artifact-name" title="${escapeHtml(i.filename)}">${escapeHtml(C)}</span>
38
+ ${F}
39
+ ${B}
40
+ ${$}
41
+ ${V}
42
+ <span class="artifact-time">${d}</span>
43
+ <span class="artifact-size">${b}</span>
1238
44
  </div>
1239
45
  <div class="artifact-actions">
1240
46
  <button class="action-btn open-btn" title="Open in editor">Open</button>
1241
47
  <button class="action-btn delete-btn" title="Delete artifact">Delete</button>
1242
- ${layoutActions}
48
+ ${q}
1243
49
  </div>
1244
- ${resizeHandle}
50
+ ${R}
1245
51
  </div>
1246
- `;
1247
- };
1248
- // Keyed card reuse: diff incoming artifacts against existing cards by localPath and only
1249
- // rebuild what changed. Rebuilding a card destroys its iframe/img and re-fetches content —
1250
- // that teardown is the visible blink — so unchanged cards must not be touched at all.
1251
- /** @type {Map<string, HTMLElement>} */
1252
- const existingCards = new Map();
1253
- Array.from(container.children).forEach((childEl) => {
1254
- const child = /** @type {HTMLElement} */ (childEl);
1255
- const childPath = child.dataset.path || '';
1256
- const reusableAsCard =
1257
- child.classList.contains('artifact-card')
1258
- && !child.classList.contains('fleet-dashboard-full') // fleet layout card: different structure
1259
- && !child.classList.contains('deleting')
1260
- && childPath
1261
- && !existingCards.has(childPath);
1262
- if (!reusableAsCard) {
1263
- child.remove();
1264
- return;
1265
- }
1266
- existingCards.set(childPath, child);
1267
- });
1268
-
1269
- /** @type {Array<object>} */
1270
- const htmlArtifactsNeedingFetch = [];
1271
- /** @type {Array<object>} */
1272
- const markdownArtifactsNeedingFetch = [];
1273
- /** @type {HTMLElement[]} */
1274
- const orderedCards = [];
1275
- for (const artifact of orderedArtifacts) {
1276
- const path = String(artifact.localPath || '');
1277
- const modifiedAt = String(artifact.modifiedAt || '');
1278
- const isVisual = artifact.type === 'chart' || artifact.type === 'image';
1279
- const chartState = isVisual ? artifact._panel : undefined;
1280
- const signature = computeCardRenderSignature(artifact);
1281
- const forceHtmlReload = forceFreshHtml && artifact.format === 'html';
1282
- const existing = existingCards.get(path);
1283
- existingCards.delete(path);
1284
-
1285
- if (existing && existing.dataset.cardSignature === signature && !forceHtmlReload) {
1286
- // Unchanged: leave the DOM (and any live iframe) alone. Expanded/height are kept
1287
- // out of the signature and applied in place so toggling them never reloads content.
1288
- if (isVisual) {
1289
- existing.classList.toggle('expanded', chartState?.expanded === true);
1290
- const contentEl = /** @type {HTMLElement | null} */ (existing.querySelector('.artifact-content'));
1291
- if (contentEl && chartState?.height) {
1292
- contentEl.style.height = `${chartState.height}px`;
1293
- contentEl.style.minHeight = `${chartState.height}px`;
1294
- contentEl.style.overflow = 'auto';
1295
- }
1296
- }
1297
- const timeEl = existing.querySelector('.artifact-time');
1298
- if (timeEl) {
1299
- timeEl.textContent = formatTimeAgo(new Date(artifact.modifiedAt));
1300
- }
1301
- orderedCards.push(existing);
1302
- continue;
1303
- }
1304
-
1305
- const template = document.createElement('template');
1306
- template.innerHTML = buildCardHtml(artifact).trim();
1307
- const card = /** @type {HTMLElement | null} */ (template.content.firstElementChild);
1308
- if (!card) {
1309
- continue;
1310
- }
1311
- card.dataset.cardSignature = signature;
1312
- const htmlHost = /** @type {HTMLElement | null} */ (card.querySelector('.html-preview'));
1313
- if (htmlHost) {
1314
- const reusable = forceHtmlReload ? undefined : reusableHtmlPreviews.get(`${path}::${modifiedAt}`);
1315
- if (reusable) {
1316
- htmlHost.classList.add('ready');
1317
- htmlHost.innerHTML = '';
1318
- htmlHost.appendChild(reusable);
1319
- } else if (artifact.format === 'html') {
1320
- htmlArtifactsNeedingFetch.push(artifact);
1321
- }
1322
- }
1323
- if (card.querySelector('.markdown-preview')) {
1324
- markdownArtifactsNeedingFetch.push(artifact);
1325
- }
1326
- attachArtifactCardHandlers(card);
1327
- if (existing) {
1328
- existing.replaceWith(card);
1329
- }
1330
- orderedCards.push(card);
1331
- }
1332
-
1333
- // Cards whose artifacts disappeared.
1334
- existingCards.forEach((card) => card.remove());
1335
-
1336
- // Minimal-move reorder: re-inserting a node reloads its iframe, so only move cards that
1337
- // are actually out of position.
1338
- let cursor = container.firstElementChild;
1339
- for (const card of orderedCards) {
1340
- if (card === cursor) {
1341
- cursor = cursor.nextElementSibling;
1342
- continue;
1343
- }
1344
- container.insertBefore(card, cursor);
1345
- }
1346
-
1347
- requestHtmlPreviewContents(htmlArtifactsNeedingFetch);
1348
- requestMarkdownPreviewContents(markdownArtifactsNeedingFetch);
1349
-
1350
- // Markdown previews are loaded via host message passing (artifactContent)
1351
- // to avoid fetch restrictions for local report files.
1352
- }
1353
-
1354
- /**
1355
- * Per-card render signature: every input that affects a card's DOM except expanded/height,
1356
- * which renderGallery applies in place so toggling them never rebuilds the card.
1357
- * @param {object} artifact
1358
- * @returns {string}
1359
- */
1360
- function computeCardRenderSignature(artifact) {
1361
- const isVisual = artifact.type === 'chart' || artifact.type === 'image';
1362
- const chartState = isVisual ? artifact._panel : undefined;
1363
- const manifestEntry = state.manifest?.artifacts?.find(e => e.filename === artifact.filename);
1364
- return [
1365
- String(artifact.localPath || ''),
1366
- String(artifact.filename || ''),
1367
- String(artifact.modifiedAt || ''),
1368
- String(artifact.size || ''),
1369
- String(artifact.type || ''),
1370
- String(artifact.format || ''),
1371
- String(artifact.generator || ''),
1372
- String(manifestEntry?.title || ''),
1373
- String(manifestEntry?.source || ''),
1374
- chartState?.hidden === true ? '1' : '0',
1375
- Number.isFinite(chartState?.order) ? String(chartState.order) : '',
1376
- ].join('|');
1377
- }
1378
-
1379
- /**
1380
- * Chart/image ordering keys derived from current DOM order — computed at click time so
1381
- * handlers on long-lived cards never act on a stale snapshot.
1382
- * @returns {string[]}
1383
- */
1384
- function getInteractiveChartKeysFromDom() {
1385
- const container = document.getElementById('gallery-content');
1386
- if (!container) {
1387
- return [];
1388
- }
1389
- return Array.from(container.querySelectorAll('.artifact-card'))
1390
- .filter((cardEl) => {
1391
- const card = /** @type {HTMLElement} */ (cardEl);
1392
- return card.dataset.type === 'chart' || card.dataset.type === 'image';
1393
- })
1394
- .map((cardEl) => normalizeFilename(String(/** @type {HTMLElement} */ (cardEl).dataset.filename || '')))
1395
- .filter(Boolean);
1396
- }
1397
-
1398
- /**
1399
- * Wire interactive behaviors for one gallery card (open/delete, layout buttons, expand on
1400
- * click, resize handle). Called once per created card; reused cards keep their listeners.
1401
- * @param {HTMLElement} card
1402
- */
1403
- function attachArtifactCardHandlers(card) {
1404
- const path = card.dataset.path;
1405
- const filename = card.dataset.filename;
1406
- const isChart = card.dataset.type === 'chart';
1407
- const isVisual = card.dataset.type === 'chart' || card.dataset.type === 'image';
1408
-
1409
- card.querySelector('.open-btn')?.addEventListener('click', (e) => {
1410
- e.stopPropagation();
1411
- panelApi.postMessage({ type: 'openArtifact', path });
1412
- });
1413
-
1414
- card.querySelector('.delete-btn')?.addEventListener('click', (e) => {
1415
- e.stopPropagation();
1416
- panelApi.postMessage({ type: 'deleteArtifact', path });
1417
- });
1418
-
1419
- if (isChart && filename) {
1420
- card.querySelector('.move-up-btn')?.addEventListener('click', (e) => {
1421
- e.stopPropagation();
1422
- moveChartInPanelState(filename, 'up', getInteractiveChartKeysFromDom());
1423
- renderGallery(state.artifacts);
1424
- });
1425
-
1426
- card.querySelector('.move-down-btn')?.addEventListener('click', (e) => {
1427
- e.stopPropagation();
1428
- moveChartInPanelState(filename, 'down', getInteractiveChartKeysFromDom());
1429
- renderGallery(state.artifacts);
1430
- });
1431
-
1432
- card.querySelector('.toggle-enabled-btn')?.addEventListener('click', (e) => {
1433
- e.stopPropagation();
1434
- toggleChartHiddenInPanelState(filename);
1435
- renderGallery(state.artifacts);
1436
- });
1437
- }
1438
-
1439
- // Click on card to expand/collapse
1440
- card.addEventListener('click', (event) => {
1441
- const target = /** @type {HTMLElement | null} */ (event.target instanceof HTMLElement ? event.target : null);
1442
- if (target?.closest('button,.resize-handle')) {
1443
- return;
1444
- }
1445
- const nextExpanded = !card.classList.contains('expanded');
1446
- card.classList.toggle('expanded', nextExpanded);
1447
- if (isVisual && filename) {
1448
- setChartExpanded(filename, nextExpanded);
1449
- }
1450
- });
1451
-
1452
- if (filename) {
1453
- attachResizeHandle(card, filename);
1454
- }
1455
- }
1456
-
1457
- /**
1458
- * Build a compact signature for artifact catalog identity.
1459
- * Used to avoid expensive no-op rerenders on duplicate updates.
1460
- * @param {Array<object>} artifacts
1461
- * @returns {string}
1462
- */
1463
- function getArtifactCatalogSignature(artifacts) {
1464
- return artifacts
1465
- .map((artifact) => [
1466
- String(artifact.localPath || ''),
1467
- String(artifact.modifiedAt || ''),
1468
- String(artifact.size || ''),
1469
- String(artifact.format || ''),
1470
- String(artifact.type || ''),
1471
- ].join('|'))
1472
- .sort()
1473
- .join('\n');
1474
- }
1475
-
1476
- /** Signature ignoring export files — chart HTML identity for preserveFleetIframe fast path. */
1477
- function getArtifactCatalogSignatureExcludingExports(artifacts) {
1478
- return getArtifactCatalogSignature(artifacts.filter((artifact) => artifact.type !== 'export'));
1479
- }
1480
-
1481
- /**
1482
- * Add nonce to inline script tags so they can run in srcdoc.
1483
- * @param {string} html
1484
- * @param {string} nonce
1485
- * @returns {string}
1486
- */
1487
- function injectNonceIntoInlineScripts(html, nonce) {
1488
- if (!nonce) {
1489
- return html;
1490
- }
1491
- return html.replace(/<script(?![^>]*\bnonce=)([^>]*)>/gi, `<script nonce="${nonce}"$1>`);
1492
- }
1493
-
1494
- /**
1495
- * Inject a keyboard shortcut bridge for embedded iframe widgets so common
1496
- * shortcuts (Ctrl/Cmd+A/C/X/V) work reliably in webview sandboxes.
1497
- * @param {string} html
1498
- * @param {string} nonce
1499
- * @returns {string}
1500
- */
1501
- function injectIframeKeyboardShortcutBridge(html, nonce) {
1502
- const bridge = `<script nonce="${nonce}">(function(){
52
+ `},x=new Map;Array.from(a.children).forEach(i=>{const d=i,b=d.dataset.path||"";if(!(d.classList.contains("artifact-card")&&!d.classList.contains("fleet-dashboard-full")&&!d.classList.contains("deleting")&&b&&!x.has(b))){d.remove();return}x.set(b,d)});const k=[],H=[],M=[];for(const i of c){const d=String(i.localPath||""),b=String(i.modifiedAt||""),g=i.type==="chart"||i.type==="image",m=g?i._panel:void 0,I=computeCardRenderSignature(i),E=n&&i.format==="html",u=x.get(d);if(x.delete(d),u&&u.dataset.cardSignature===I&&!E){if(g){u.classList.toggle("expanded",m?.expanded===!0);const C=u.querySelector(".artifact-content");C&&m?.height&&(C.style.height=`${m.height}px`,C.style.minHeight=`${m.height}px`,C.style.overflow="auto")}const h=u.querySelector(".artifact-time");h&&(h.textContent=formatTimeAgo(new Date(i.modifiedAt))),M.push(u);continue}const A=document.createElement("template");A.innerHTML=S(i).trim();const w=A.content.firstElementChild;if(!w)continue;w.dataset.cardSignature=I;const y=w.querySelector(".html-preview");if(y){const h=E?void 0:r.get(`${d}::${b}`);h?(y.classList.add("ready"),y.innerHTML="",y.appendChild(h)):i.format==="html"&&k.push(i)}w.querySelector(".markdown-preview")&&H.push(i),attachArtifactCardHandlers(w),u&&u.replaceWith(w),M.push(w)}x.forEach(i=>i.remove());let T=a.firstElementChild;for(const i of M){if(i===T){T=T.nextElementSibling;continue}a.insertBefore(i,T)}requestHtmlPreviewContents(k),requestMarkdownPreviewContents(H)}function computeCardRenderSignature(e){const n=e.type==="chart"||e.type==="image"?e._panel:void 0,a=state.manifest?.artifacts?.find(r=>r.filename===e.filename);return[String(e.localPath||""),String(e.filename||""),String(e.modifiedAt||""),String(e.size||""),String(e.type||""),String(e.format||""),String(e.generator||""),String(a?.title||""),String(a?.source||""),n?.hidden===!0?"1":"0",Number.isFinite(n?.order)?String(n.order):""].join("|")}function getInteractiveChartKeysFromDom(){const e=document.getElementById("gallery-content");return e?Array.from(e.querySelectorAll(".artifact-card")).filter(t=>{const n=t;return n.dataset.type==="chart"||n.dataset.type==="image"}).map(t=>normalizeFilename(String(t.dataset.filename||""))).filter(Boolean):[]}function attachArtifactCardHandlers(e){const t=e.dataset.path,n=e.dataset.filename,a=e.dataset.type==="chart",r=e.dataset.type==="chart"||e.dataset.type==="image";e.querySelector(".open-btn")?.addEventListener("click",s=>{s.stopPropagation(),panelApi.postMessage({type:"openArtifact",path:t})}),e.querySelector(".delete-btn")?.addEventListener("click",s=>{s.stopPropagation(),panelApi.postMessage({type:"deleteArtifact",path:t})}),a&&n&&(e.querySelector(".move-up-btn")?.addEventListener("click",s=>{s.stopPropagation(),moveChartInPanelState(n,"up",getInteractiveChartKeysFromDom()),renderGallery(state.artifacts)}),e.querySelector(".move-down-btn")?.addEventListener("click",s=>{s.stopPropagation(),moveChartInPanelState(n,"down",getInteractiveChartKeysFromDom()),renderGallery(state.artifacts)}),e.querySelector(".toggle-enabled-btn")?.addEventListener("click",s=>{s.stopPropagation(),toggleChartHiddenInPanelState(n),renderGallery(state.artifacts)})),e.addEventListener("click",s=>{if((s.target instanceof HTMLElement?s.target:null)?.closest("button,.resize-handle"))return;const f=!e.classList.contains("expanded");e.classList.toggle("expanded",f),r&&n&&setChartExpanded(n,f)}),n&&attachResizeHandle(e,n)}function getArtifactCatalogSignature(e){return e.map(t=>[String(t.localPath||""),String(t.modifiedAt||""),String(t.size||""),String(t.format||""),String(t.type||"")].join("|")).sort().join(`
53
+ `)}function getArtifactCatalogSignatureExcludingExports(e){return getArtifactCatalogSignature(e.filter(t=>t.type!=="export"))}function injectNonceIntoInlineScripts(e,t){return t?e.replace(/<script(?![^>]*\bnonce=)([^>]*)>/gi,`<script nonce="${t}"$1>`):e}function injectIframeKeyboardShortcutBridge(e,t){const n=`<script nonce="${t}">(function(){
1503
54
  try {
1504
55
  var pending = new Map();
1505
56
  function requestClipboardText() {
@@ -1668,839 +219,4 @@ function injectIframeKeyboardShortcutBridge(html, nonce) {
1668
219
  } catch (err) {
1669
220
  // no-op
1670
221
  }
1671
- })();</script>`;
1672
- if (/<\/body>/i.test(html)) {
1673
- return html.replace(/<\/body>/i, `${bridge}</body>`);
1674
- }
1675
- return `${html}${bridge}`;
1676
- }
1677
-
1678
- /**
1679
- * Ask host to read HTML artifact contents by local path.
1680
- * This avoids webview URL fetch/DNS issues for nested iframe previews.
1681
- * @param {Array<object>} artifacts
1682
- */
1683
- function requestHtmlPreviewContents(artifacts) {
1684
- for (const artifact of artifacts) {
1685
- if (artifact.format !== 'html' || !artifact.localPath) {
1686
- continue;
1687
- }
1688
- panelApi.postMessage({
1689
- type: 'requestArtifactContent',
1690
- path: artifact.localPath,
1691
- format: 'html',
1692
- });
1693
- }
1694
- }
1695
-
1696
- /**
1697
- * Ask host to read markdown artifact contents by local path.
1698
- * @param {Array<object>} artifacts
1699
- */
1700
- function requestMarkdownPreviewContents(artifacts) {
1701
- for (const artifact of artifacts) {
1702
- if (artifact.format !== 'md' || !artifact.localPath) {
1703
- continue;
1704
- }
1705
- panelApi.postMessage({
1706
- type: 'requestArtifactContent',
1707
- path: artifact.localPath,
1708
- format: 'md',
1709
- });
1710
- }
1711
- }
1712
-
1713
- /**
1714
- * Render HTML preview for one artifact path from host-provided content.
1715
- * @param {string} path
1716
- * @param {string | undefined} content
1717
- * @param {string | undefined} error
1718
- */
1719
- function renderHtmlPreview(path, content, error) {
1720
- const card = Array.from(document.querySelectorAll('.artifact-card')).find((cardEl) => {
1721
- const element = /** @type {HTMLElement} */ (cardEl);
1722
- return element.dataset.path === path;
1723
- });
1724
- const host = /** @type {HTMLElement | null} */ (card?.querySelector('.html-preview'));
1725
- if (!host) {
1726
- return;
1727
- }
1728
-
1729
- if (error || !content) {
1730
- const reason = error || 'No preview content available.';
1731
- host.innerHTML = `<div class="html-preview-fallback">Preview unavailable (${escapeHtml(reason)}). Use <strong>Open</strong> to inspect the artifact.</div>`;
1732
- return;
1733
- }
1734
-
1735
- const scriptNonce = document.querySelector('script[nonce]')?.getAttribute('nonce') || '';
1736
- const iframe = document.createElement('iframe');
1737
- iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
1738
- iframe.setAttribute('allow', 'clipboard-read; clipboard-write');
1739
- iframe.setAttribute('loading', 'lazy');
1740
- iframe.tabIndex = 0;
1741
- const contentWithNonce = injectNonceIntoInlineScripts(content, scriptNonce);
1742
- iframe.srcdoc = injectIframeKeyboardShortcutBridge(contentWithNonce, scriptNonce);
1743
- iframe.addEventListener('load', () => {
1744
- host.classList.add('ready');
1745
- const parentCard = host.closest('.artifact-card');
1746
- if (parentCard instanceof HTMLElement && parentCard.classList.contains('fleet-dashboard-full')) {
1747
- autoFitFleetCardHeight(parentCard);
1748
- }
1749
- });
1750
- host.innerHTML = '';
1751
- host.appendChild(iframe);
1752
- }
1753
-
1754
- // -----------------------------------------------------------------------------
1755
- // Quick Prompts & Ask AI
1756
- // -----------------------------------------------------------------------------
1757
-
1758
- function initQuickPrompts() {
1759
- // Quick prompt buttons in empty state
1760
- document.querySelectorAll('.quick-prompt').forEach(buttonEl => {
1761
- const btn = /** @type {HTMLElement} */ (buttonEl);
1762
- btn.addEventListener('click', () => {
1763
- const prompt = btn.dataset.prompt;
1764
- if (prompt) {
1765
- panelApi.postMessage({ type: 'openChatWithPrompt', prompt });
1766
- }
1767
- });
1768
- });
1769
-
1770
- // Main "Ask AI" button
1771
- document.getElementById('ask-ai-btn')?.addEventListener('click', () => {
1772
- panelApi.postMessage({ type: 'openChat' });
1773
- });
1774
-
1775
- // Refresh button
1776
- document.getElementById('refresh-charts-btn')?.addEventListener('click', () => {
1777
- refreshCurrentView();
1778
- });
1779
-
1780
- // Remove legacy Expand/Shrink buttons if a user-customized panel.html still contains them.
1781
- document.getElementById('expand-sidebar-btn')?.remove();
1782
- document.getElementById('shrink-sidebar-btn')?.remove();
1783
-
1784
- document.getElementById('summary-toggle-btn')?.addEventListener('click', (e) => {
1785
- e.stopPropagation();
1786
- toggleSummarySection();
1787
- });
1788
- applySummarySectionVisibility();
1789
- }
1790
-
1791
- // -----------------------------------------------------------------------------
1792
- // Filters
1793
- // -----------------------------------------------------------------------------
1794
-
1795
- function applyFiltersToControls() {
1796
- const agentSelect = /** @type {HTMLSelectElement | null} */ (document.getElementById('agent-filter-select'));
1797
- if (agentSelect) {
1798
- agentSelect.innerHTML = '<option value="">All agents</option>';
1799
- for (const agentId of state.agentIds) {
1800
- const option = document.createElement('option');
1801
- option.value = agentId;
1802
- option.textContent = agentId;
1803
- agentSelect.appendChild(option);
1804
- }
1805
- agentSelect.value = state.agentId || '';
1806
- }
1807
- }
1808
-
1809
- function postFilterUpdate() {
1810
- panelApi.postMessage({
1811
- type: 'updateFilters',
1812
- agentId: state.agentId || '',
1813
- });
1814
- }
1815
-
1816
- function initFilters() {
1817
- const agentSelect = /** @type {HTMLSelectElement | null} */ (document.getElementById('agent-filter-select'));
1818
- agentSelect?.addEventListener('change', () => {
1819
- state.agentId = agentSelect.value || '';
1820
- postFilterUpdate();
1821
- });
1822
- }
1823
-
1824
- // -----------------------------------------------------------------------------
1825
- // State Actions
1826
- // -----------------------------------------------------------------------------
1827
-
1828
- function initStateActions() {
1829
- // Cancel generation
1830
- document.getElementById('cancel-generate-btn')?.addEventListener('click', () => {
1831
- panelApi.postMessage({ type: 'cancelGeneration' });
1832
- });
1833
-
1834
- // Retry generation
1835
- document.getElementById('retry-generate-btn')?.addEventListener('click', () => {
1836
- panelApi.postMessage({ type: 'retryGeneration' });
1837
- });
1838
- }
1839
-
1840
- // -----------------------------------------------------------------------------
1841
- // Toolbar Menu
1842
- // -----------------------------------------------------------------------------
1843
-
1844
- function initToolbarMenu() {
1845
- const menuBtn = document.getElementById('toolbar-menu-btn');
1846
- const dropdown = document.getElementById('toolbar-menu-dropdown');
1847
-
1848
- if (!menuBtn || !dropdown) {
1849
- return;
1850
- }
1851
-
1852
- /**
1853
- * Toggle dropdown visibility.
1854
- * @param {boolean} [forceOpen]
1855
- */
1856
- function toggleMenu(forceOpen) {
1857
- const isOpen = forceOpen ?? !dropdown.classList.contains('open');
1858
- dropdown.classList.toggle('open', isOpen);
1859
- dropdown.setAttribute('aria-hidden', String(!isOpen));
1860
- menuBtn.setAttribute('aria-expanded', String(isOpen));
1861
- }
1862
-
1863
- // Toggle on button click
1864
- menuBtn.addEventListener('click', (e) => {
1865
- e.stopPropagation();
1866
- toggleMenu();
1867
- });
1868
-
1869
- // Close when clicking outside
1870
- document.addEventListener('click', (e) => {
1871
- if (!dropdown.contains(/** @type {Node} */(e.target)) && e.target !== menuBtn) {
1872
- toggleMenu(false);
1873
- }
1874
- });
1875
-
1876
- // Close on Escape
1877
- document.addEventListener('keydown', (e) => {
1878
- if (e.key === 'Escape' && dropdown.classList.contains('open')) {
1879
- toggleMenu(false);
1880
- menuBtn.focus();
1881
- }
1882
- });
1883
-
1884
- // Handle menu item clicks
1885
- dropdown.querySelectorAll('.dropdown-item[data-action]').forEach((itemEl) => {
1886
- const item = /** @type {HTMLElement} */ (itemEl);
1887
- item.addEventListener('click', () => {
1888
- const action = item.dataset.action;
1889
- toggleMenu(false);
1890
- const activeView = getActiveView();
1891
-
1892
- switch (action) {
1893
- case 'new-view':
1894
- promptCreateView();
1895
- break;
1896
-
1897
- case 'duplicate-view':
1898
- if (activeView?.id) {
1899
- panelApi.postMessage({ type: 'duplicateView', viewId: activeView.id });
1900
- }
1901
- break;
1902
-
1903
- case 'rename-view': {
1904
- if (!activeView || activeView.system) {
1905
- break;
1906
- }
1907
- panelApi.postMessage({ type: 'requestRenameView', viewId: activeView.id });
1908
- break;
1909
- }
1910
-
1911
- case 'delete-view': {
1912
- if (!activeView || activeView.system) {
1913
- break;
1914
- }
1915
- panelApi.postMessage({ type: 'requestDeleteView', viewId: activeView.id });
1916
- break;
1917
- }
1918
-
1919
- case 'edit-view-scripts':
1920
- panelApi.postMessage({ type: 'editViewScripts', viewId: activeView?.id });
1921
- break;
1922
-
1923
- case 'share-scripts-only':
1924
- onShareScriptsOnlyClick();
1925
- break;
1926
-
1927
- case 'refresh-all-views':
1928
- panelApi.postMessage({ type: 'refreshAllViews' });
1929
- break;
1930
-
1931
- case 'reset-home':
1932
- if (confirm('Reset all charts and scripts in your home analytics folder? This will delete generated charts and user scripts, then regenerate defaults.')) {
1933
- panelApi.postMessage({ type: 'resetChartsHome' });
1934
- }
1935
- break;
1936
-
1937
- case 'reset-workspace':
1938
- if (confirm('Reset all charts and scripts in this workspace? This will delete generated charts and user scripts for the current workspace, then regenerate defaults.')) {
1939
- panelApi.postMessage({ type: 'resetChartsWorkspace' });
1940
- }
1941
- break;
1942
-
1943
- case 'reenable-charts':
1944
- panelApi.postMessage({ type: 'reenableAllCharts' });
1945
- break;
1946
- }
1947
- });
1948
- });
1949
- }
1950
-
1951
- /**
1952
- * Push a view export snapshot into embedded dashboard iframes (no iframe reload).
1953
- * Only when the active view has preserveFleetIframe (host already gates pushes).
1954
- * @param {{ payload?: object, meta?: object }} message
1955
- */
1956
- function postViewExportSnapshotToDashboardIframes(message) {
1957
- const payload = message.payload;
1958
- const meta = message.meta;
1959
- document.querySelectorAll('.html-preview iframe').forEach((frameEl) => {
1960
- const iframe = /** @type {HTMLIFrameElement} */ (frameEl);
1961
- try {
1962
- iframe.contentWindow?.postMessage({ type: VIEW_EXPORT_SNAPSHOT, payload, meta }, '*');
1963
- // Legacy widgets (e.g. live-fleet-2.html) still listen for liveFleetSnapshot.
1964
- iframe.contentWindow?.postMessage({ type: LEGACY_VIEW_EXPORT_SNAPSHOT, payload, meta }, '*');
1965
- } catch {
1966
- // Cross-origin or unloaded iframe — ignore.
1967
- }
1968
- });
1969
- }
1970
-
1971
- /**
1972
- * Bridge postMessage events from embedded HTML widgets.
1973
- * Allows widgets to trigger host actions (e.g. copy session id).
1974
- */
1975
- function initEmbeddedWidgetBridge() {
1976
- window.addEventListener('message', (event) => {
1977
- const data = event.data;
1978
- if (!data || typeof data !== 'object') {
1979
- return;
1980
- }
1981
- if (data.source !== 'agentide-wow-widget') {
1982
- return;
1983
- }
1984
- if (data.type === 'copySessionId' && typeof data.sessionId === 'string') {
1985
- const sessionId = data.sessionId.trim();
1986
- if (sessionId) {
1987
- panelApi.postMessage({ type: 'copySessionId', sessionId });
1988
- }
1989
- return;
1990
- }
1991
- if (data.type === 'requestClipboardText' && typeof data.requestId === 'string') {
1992
- const requestId = data.requestId.trim();
1993
- if (requestId) {
1994
- panelApi.postMessage({ type: 'requestClipboardText', requestId });
1995
- }
1996
- return;
1997
- }
1998
- if (data.type === 'writeClipboardText' && typeof data.text === 'string') {
1999
- panelApi.postMessage({ type: 'writeClipboardText', text: data.text });
2000
- return;
2001
- }
2002
- if (data.type === 'openArtifact' && typeof data.path === 'string') {
2003
- const artifactPath = data.path.trim();
2004
- if (artifactPath) {
2005
- panelApi.postMessage({ type: 'openArtifact', path: artifactPath });
2006
- }
2007
- return;
2008
- }
2009
- if (data.type === 'openChatWithPrompt' && typeof data.prompt === 'string') {
2010
- const prompt = data.prompt.trim();
2011
- if (prompt) {
2012
- panelApi.postMessage({
2013
- type: 'openChatWithPrompt',
2014
- prompt: prompt.slice(0, 12_000),
2015
- });
2016
- }
2017
- return;
2018
- }
2019
- if (data.type === 'disconnectFleetChannel' && typeof data.sessionId === 'string') {
2020
- const sessionId = data.sessionId.trim();
2021
- if (sessionId) {
2022
- panelApi.postMessage({
2023
- type: 'disconnectFleetChannel',
2024
- sessionId,
2025
- channelType: typeof data.channelType === 'string' ? data.channelType : '',
2026
- channelTarget: typeof data.channelTarget === 'string' ? data.channelTarget : '',
2027
- });
2028
- }
2029
- return;
2030
- }
2031
- if (data.type === 'updateFleetCardTitle' && typeof data.sessionId === 'string' && typeof data.cardTitle === 'string') {
2032
- const sessionId = data.sessionId.trim();
2033
- const sourceId = typeof data.sourceId === 'string' ? data.sourceId.trim() : 'local';
2034
- if (sessionId) {
2035
- panelApi.postMessage({
2036
- type: 'updateFleetCardTitle',
2037
- sourceId,
2038
- sessionId,
2039
- cardTitle: data.cardTitle,
2040
- });
2041
- }
2042
- return;
2043
- }
2044
- if (data.type === 'openSpotlightForSession' && typeof data.sessionId === 'string') {
2045
- const sessionId = data.sessionId.trim();
2046
- if (sessionId) {
2047
- panelApi.postMessage({
2048
- type: 'openSpotlightForSession',
2049
- sessionId,
2050
- channelType: typeof data.channelType === 'string' ? data.channelType : '',
2051
- channelTarget: typeof data.channelTarget === 'string' ? data.channelTarget : '',
2052
- model: typeof data.model === 'string' ? data.model : '',
2053
- engine: typeof data.engine === 'string' ? data.engine : '',
2054
- agentId: typeof data.agentId === 'string' ? data.agentId : '',
2055
- });
2056
- }
2057
- return;
2058
- }
2059
- if (data.type === 'refreshView') {
2060
- panelApi.postMessage({ type: 'refreshView' });
2061
- return;
2062
- }
2063
- if (data.type === 'refreshFleetWidget') {
2064
- panelApi.postMessage({ type: 'refreshFleetWidget' });
2065
- return;
2066
- }
2067
- if (data.type === 'refreshWidget') {
2068
- const script = typeof data.script === 'string' ? data.script.trim() : '';
2069
- const scripts = Array.isArray(data.scripts)
2070
- ? data.scripts.filter((s) => typeof s === 'string').map((s) => s.trim()).filter(Boolean)
2071
- : [];
2072
- if (!script && scripts.length === 0) {
2073
- return;
2074
- }
2075
- panelApi.postMessage({
2076
- type: 'refreshWidget',
2077
- script: script || undefined,
2078
- scripts: scripts.length > 0 ? scripts : undefined,
2079
- });
2080
- return;
2081
- }
2082
- if (data.type === REQUEST_VIEW_EXPORT_SNAPSHOT || data.type === LEGACY_REQUEST_LIVE_FLEET_SNAPSHOT) {
2083
- panelApi.postMessage({ type: REQUEST_VIEW_EXPORT_SNAPSHOT });
2084
- return;
2085
- }
2086
- if (data.type === 'openChat') {
2087
- panelApi.postMessage({ type: 'openChat' });
2088
- }
2089
- });
2090
-
2091
- // Fallback: forward openSpotlightForSession when message is from a frame that didn't set
2092
- // source (e.g. fleet iframe in some contexts) so card click always opens chat
2093
- window.addEventListener('message', (event) => {
2094
- const data = event.data;
2095
- if (!data || typeof data !== 'object' || data.type !== 'openSpotlightForSession') {
2096
- return;
2097
- }
2098
- if (data.source === 'agentide-wow-widget') {
2099
- return; // already handled above
2100
- }
2101
- if (typeof data.sessionId !== 'string') {
2102
- return;
2103
- }
2104
- const sessionId = data.sessionId.trim();
2105
- if (!sessionId) {
2106
- return;
2107
- }
2108
- panelApi.postMessage({
2109
- type: 'openSpotlightForSession',
2110
- sessionId,
2111
- channelType: typeof data.channelType === 'string' ? data.channelType : '',
2112
- channelTarget: typeof data.channelTarget === 'string' ? data.channelTarget : '',
2113
- model: typeof data.model === 'string' ? data.model : '',
2114
- engine: typeof data.engine === 'string' ? data.engine : '',
2115
- agentId: typeof data.agentId === 'string' ? data.agentId : '',
2116
- });
2117
- });
2118
- }
2119
-
2120
- /**
2121
- * Show generating state
2122
- */
2123
- function showGeneratingState() {
2124
- showGalleryState('generating');
2125
- }
2126
-
2127
- /**
2128
- * Show failed state with error message
2129
- * @param {string} errorMessage
2130
- */
2131
- function showFailedState(errorMessage) {
2132
- const errorEl = document.getElementById('error-message');
2133
- if (errorEl) {
2134
- errorEl.textContent = errorMessage;
2135
- }
2136
- showGalleryState('failed');
2137
- }
2138
-
2139
- /**
2140
- * Show refresh hint
2141
- */
2142
- function showRefreshHint() {
2143
- const hint = document.getElementById('refresh-hint');
2144
- if (hint) {
2145
- hint.style.display = 'flex';
2146
- }
2147
- }
2148
-
2149
- /**
2150
- * Hide refresh hint
2151
- */
2152
- function hideRefreshHint() {
2153
- const hint = document.getElementById('refresh-hint');
2154
- if (hint) {
2155
- hint.style.display = 'none';
2156
- }
2157
- }
2158
-
2159
- /**
2160
- * Show/hide per-chart refresh indicator.
2161
- * @param {boolean} isRefreshing
2162
- */
2163
- function setPerChartRefreshing(isRefreshing) {
2164
- document.querySelectorAll('.artifact-card[data-type="chart"]').forEach(card => {
2165
- card.classList.toggle('chart-refreshing', isRefreshing);
2166
- let indicator = card.querySelector('.artifact-refresh-indicator');
2167
-
2168
- if (isRefreshing) {
2169
- if (!indicator) {
2170
- indicator = document.createElement('div');
2171
- indicator.className = 'artifact-refresh-indicator';
2172
- indicator.textContent = 'Refreshing';
2173
- card.appendChild(indicator);
2174
- }
2175
- } else if (indicator) {
2176
- indicator.remove();
2177
- }
2178
- });
2179
- }
2180
-
2181
- /**
2182
- * Toggle refresh button busy state.
2183
- * @param {boolean} isRefreshing
2184
- */
2185
- function setRefreshButtonState(isRefreshing) {
2186
- const btn = document.getElementById('refresh-charts-btn');
2187
- if (!btn) {
2188
- return;
2189
- }
2190
- btn.classList.toggle('refreshing', isRefreshing);
2191
- }
2192
-
2193
- // -----------------------------------------------------------------------------
2194
- // Message Handling
2195
- // -----------------------------------------------------------------------------
2196
-
2197
- panelApi.onMessage(msg => {
2198
- switch (msg.type) {
2199
- case 'artifactsUpdated': {
2200
- const nextSignature = getArtifactCatalogSignature(msg.artifacts);
2201
- const nextActiveViewId = typeof msg.activeViewId === 'string' ? msg.activeViewId : state.activeViewId;
2202
- const nextFleetRefreshVersion = typeof msg.fleetRefreshVersion === 'number'
2203
- ? Math.max(0, Math.floor(msg.fleetRefreshVersion))
2204
- : state.fleetRefreshVersion;
2205
- const nextGalleryIgnore = Array.isArray(msg.galleryIgnorePatterns)
2206
- ? msg.galleryIgnorePatterns.filter((p) => typeof p === 'string').map((p) => String(p).trim()).filter(Boolean)
2207
- : [];
2208
- const isFleetView = nextActiveViewId === 'fleet';
2209
- const exportOnlyCatalogChange =
2210
- activeViewPreservesFleetIframe()
2211
- && nextSignature !== state.lastArtifactCatalogSignature
2212
- && getArtifactCatalogSignatureExcludingExports(msg.artifacts)
2213
- === getArtifactCatalogSignatureExcludingExports(state.artifacts);
2214
- // preserveFleetIframe views (default for fleet-dashboard layout) keep the iframe mounted
2215
- // and rely on viewExportSnapshot postMessages for updates — the widget's DOM-diff
2216
- // renderer applies them in-place. Non-preserve flows still reload when version bumps.
2217
- const hasFleetVersionUpdate =
2218
- isFleetView
2219
- && nextFleetRefreshVersion > state.fleetRefreshVersion
2220
- && !activeViewPreservesFleetIframe();
2221
- // Skip only when signature unchanged and not refreshing, unless Fleet explicitly signaled fresher data.
2222
- if (nextSignature === state.lastArtifactCatalogSignature && !state.isRefreshing && !hasFleetVersionUpdate) {
2223
- break;
2224
- }
2225
- state.lastArtifactCatalogSignature = nextSignature;
2226
- state.artifacts = msg.artifacts;
2227
- state.galleryIgnorePatterns = nextGalleryIgnore;
2228
- // View selection is owned by viewsLoaded; do not let artifact batches revert the dropdown.
2229
- const prevActiveViewId = state.activeViewId;
2230
- if (
2231
- nextActiveViewId === state.activeViewId
2232
- || !state.views.some((view) => view.id === state.activeViewId)
2233
- ) {
2234
- state.activeViewId = nextActiveViewId;
2235
- }
2236
- if (state.activeViewId !== prevActiveViewId) {
2237
- // Host-initiated view change (e.g. openWithView): any in-flight refresh belongs
2238
- // to the previous view — render now instead of deferring behind it.
2239
- resetRefreshTrackingForViewChange();
2240
- }
2241
- state.fleetRefreshVersion = nextFleetRefreshVersion;
2242
- applyFleetAutoRefresh();
2243
- if (exportOnlyCatalogChange) {
2244
- // Export JSON changed; host already pushes viewExportSnapshot — avoid gallery/iframe rebuild.
2245
- break;
2246
- }
2247
- // Fleet JSON can change without agent-fleet.html mtime changing if the host
2248
- // did not re-run the widget script; bumping fleetRefreshVersion means data is
2249
- // fresher — force a preview reload so the iframe is not reused with stale HTML.
2250
- if (state.isRefreshing) {
2251
- // Mid-refresh burst: keep spinners on; requestGalleryRender defers the rebuild.
2252
- setPerChartRefreshing(true);
2253
- }
2254
- requestGalleryRender({ forceFreshHtml: hasFleetVersionUpdate });
2255
- break;
2256
- }
2257
-
2258
- case 'artifactDeleted': {
2259
- // Remove from gallery with animation
2260
- const card = document.querySelector(`[data-path="${msg.path}"]`);
2261
- if (card) {
2262
- card.classList.add('deleting');
2263
- setTimeout(() => card.remove(), 300);
2264
-
2265
- // Update state
2266
- state.artifacts = state.artifacts.filter(a => a.localPath !== msg.path);
2267
-
2268
- // Check if gallery is now empty
2269
- if (state.artifacts.length === 0) {
2270
- showGalleryState('empty');
2271
- }
2272
- }
2273
- break;
2274
- }
2275
-
2276
- case 'summaryCards':
2277
- state.summaryCards = msg.cards;
2278
- renderSummaryCards(msg.cards);
2279
- break;
2280
-
2281
- case 'manifestUpdated':
2282
- state.manifest = msg.manifest;
2283
- // Re-render gallery with new manifest data
2284
- if (state.artifacts.length > 0) {
2285
- requestGalleryRender();
2286
- }
2287
- break;
2288
-
2289
- case VIEW_EXPORT_SNAPSHOT:
2290
- case LEGACY_VIEW_EXPORT_SNAPSHOT:
2291
- postViewExportSnapshotToDashboardIframes(msg);
2292
- break;
2293
-
2294
- case 'artifactContent':
2295
- if (msg.path && msg.format === 'html') {
2296
- renderHtmlPreview(msg.path, msg.content, msg.error);
2297
- }
2298
- if (msg.path && msg.format === 'md') {
2299
- const card = Array.from(document.querySelectorAll('.artifact-card')).find((cardEl) => {
2300
- const element = /** @type {HTMLElement} */ (cardEl);
2301
- return element.dataset.path === msg.path;
2302
- });
2303
- const host = /** @type {HTMLElement | null} */ (card?.querySelector('.markdown-preview'));
2304
- if (host) {
2305
- if (msg.error || !msg.content) {
2306
- host.innerHTML = '<em>Failed to load preview</em>';
2307
- } else {
2308
- host.innerHTML = renderMarkdown(msg.content);
2309
- }
2310
- }
2311
- }
2312
- break;
2313
-
2314
- case 'clipboardText':
2315
- document.querySelectorAll('.html-preview iframe').forEach((frame) => {
2316
- const iframe = /** @type {HTMLIFrameElement} */ (frame);
2317
- iframe.contentWindow?.postMessage({
2318
- source: 'agentide-panel',
2319
- type: 'clipboardText',
2320
- requestId: msg.requestId,
2321
- text: typeof msg.text === 'string' ? msg.text : '',
2322
- }, '*');
2323
- });
2324
- break;
2325
-
2326
- case 'generatingStarted':
2327
- setRefreshButtonState(true);
2328
- state.isRefreshing = false;
2329
- state.refreshingViewId = null;
2330
- // Drop any deferred render — a later flush would stomp the generating state.
2331
- state.needsGalleryRender = false;
2332
- state.pendingForceFreshHtml = false;
2333
- setPerChartRefreshing(false);
2334
- showGeneratingState();
2335
- break;
2336
-
2337
- case 'generatingCancelled':
2338
- setRefreshButtonState(false);
2339
- state.isRefreshing = false;
2340
- state.refreshingViewId = null;
2341
- state.needsGalleryRender = false;
2342
- state.pendingForceFreshHtml = false;
2343
- setPerChartRefreshing(false);
2344
- // Go back to empty or content state
2345
- if (state.artifacts.length > 0) {
2346
- renderGallery(state.artifacts);
2347
- } else {
2348
- showGalleryState('empty');
2349
- }
2350
- break;
2351
-
2352
- case 'generatingFailed':
2353
- setRefreshButtonState(false);
2354
- state.isRefreshing = false;
2355
- state.refreshingViewId = null;
2356
- state.needsGalleryRender = false;
2357
- state.pendingForceFreshHtml = false;
2358
- setPerChartRefreshing(false);
2359
- showFailedState(msg.error);
2360
- break;
2361
-
2362
- case 'refreshStarted':
2363
- if (!msg.viewId || msg.viewId === state.activeViewId) {
2364
- setRefreshButtonState(true);
2365
- state.isRefreshing = true;
2366
- state.refreshingViewId = typeof msg.viewId === 'string' && msg.viewId ? msg.viewId : null;
2367
- setPerChartRefreshing(true);
2368
- }
2369
- break;
2370
-
2371
- case 'refreshFinished':
2372
- if (!msg.viewId || msg.viewId === state.activeViewId || msg.viewId === state.refreshingViewId) {
2373
- setRefreshButtonState(false);
2374
- state.isRefreshing = false;
2375
- state.refreshingViewId = null;
2376
- setPerChartRefreshing(false);
2377
- flushPendingGalleryRender();
2378
- if (activeViewPreservesFleetIframe()) {
2379
- document.querySelectorAll('.html-preview iframe').forEach((frameEl) => {
2380
- const iframe = /** @type {HTMLIFrameElement} */ (frameEl);
2381
- try {
2382
- iframe.contentWindow?.postMessage({ type: 'fleetRefreshComplete' }, '*');
2383
- } catch {
2384
- // Cross-origin or unloaded iframe — ignore.
2385
- }
2386
- });
2387
- }
2388
- }
2389
- break;
2390
-
2391
- case 'viewsLoaded': {
2392
- if (msg.surfaceMode === 'team' || msg.surfaceMode === 'spotlight') {
2393
- applySurfaceMode(msg.surfaceMode);
2394
- }
2395
- const prevViewsLoadedViewId = state.activeViewId;
2396
- state.views = Array.isArray(msg.views) ? msg.views : [];
2397
- state.activeViewId = typeof msg.activeViewId === 'string' ? msg.activeViewId : state.activeViewId;
2398
- if (state.activeViewId !== prevViewsLoadedViewId) {
2399
- resetRefreshTrackingForViewChange();
2400
- }
2401
- applyViewOptions();
2402
- applyFleetAutoRefresh();
2403
- break;
2404
- }
2405
-
2406
- case 'refreshNoData':
2407
- if (!msg.viewId || msg.viewId === state.activeViewId || msg.viewId === state.refreshingViewId) {
2408
- setRefreshButtonState(false);
2409
- state.isRefreshing = false;
2410
- state.refreshingViewId = null;
2411
- setPerChartRefreshing(false);
2412
- flushPendingGalleryRender();
2413
- }
2414
- break;
2415
-
2416
- case 'refreshFailed':
2417
- if (!msg.viewId || msg.viewId === state.activeViewId || msg.viewId === state.refreshingViewId) {
2418
- setRefreshButtonState(false);
2419
- state.isRefreshing = false;
2420
- state.refreshingViewId = null;
2421
- setPerChartRefreshing(false);
2422
- flushPendingGalleryRender();
2423
- if (state.artifacts.length === 0) {
2424
- showFailedState(msg.error);
2425
- }
2426
- }
2427
- break;
2428
-
2429
- case 'filtersUpdated':
2430
- state.scope = msg.scope === 'workspace' ? 'workspace' : 'global';
2431
- state.projectId = msg.projectId || '';
2432
- state.agentId = msg.agentId || '';
2433
- state.agentIds = Array.isArray(msg.agentIds) ? msg.agentIds : [];
2434
- // Note: Don't override activeViewId from filtersUpdated. This prevents race conditions
2435
- // where filtersUpdated (sent last) overwrites the correct activeViewId set by viewsLoaded.
2436
- // We still call applyViewOptions() to keep the dropdown UI in sync with the views list.
2437
- applyViewOptions();
2438
- applyFleetAutoRefresh();
2439
- applyFiltersToControls();
2440
- break;
2441
-
2442
- case 'panelStateLoaded':
2443
- if (msg.panelId && msg.panelId !== PANEL_ID) {
2444
- break;
2445
- }
2446
- if (msg.surfaceMode === 'team' || msg.surfaceMode === 'spotlight') {
2447
- applySurfaceMode(msg.surfaceMode);
2448
- }
2449
- state.panelState = normalizePanelState(msg.state);
2450
- state.panelStateLoaded = true;
2451
- applySummarySectionVisibility();
2452
- if (typeof msg.fleetAutoRefreshIntervalSeconds === 'number') {
2453
- state.fleetAutoRefreshIntervalSeconds = Math.max(0, msg.fleetAutoRefreshIntervalSeconds);
2454
- }
2455
- applyFleetAutoRefresh();
2456
- if (ensurePanelStateForArtifacts(state.artifacts)) {
2457
- savePanelState();
2458
- }
2459
- if (state.artifacts.length > 0) {
2460
- renderGallery(state.artifacts);
2461
- }
2462
- break;
2463
-
2464
- case 'disabledChartsUpdated':
2465
- state.disabledCharts = new Set(
2466
- (Array.isArray(msg.charts) ? msg.charts : [])
2467
- .map((name) => String(name || '').trim().toLowerCase())
2468
- .filter(Boolean),
2469
- );
2470
- requestGalleryRender();
2471
- break;
2472
-
2473
- case 'dataStale':
2474
- showRefreshHint();
2475
- break;
2476
-
2477
- case 'dataFresh':
2478
- hideRefreshHint();
2479
- break;
2480
- }
2481
- });
2482
-
2483
- // -----------------------------------------------------------------------------
2484
- // Initialization
2485
- // -----------------------------------------------------------------------------
2486
-
2487
- document.addEventListener('DOMContentLoaded', () => {
2488
- if (document.body.classList.contains('spotlight-surface-team')) {
2489
- applySurfaceMode('team');
2490
- }
2491
- ensureShareToolbarButton();
2492
- initQuickPrompts();
2493
- initStateActions();
2494
- initEmbeddedWidgetBridge();
2495
- initFilters();
2496
- initViews();
2497
- initToolbarMenu();
2498
- renderSummaryPlaceholders();
2499
-
2500
- // Request initial data
2501
- panelApi.postMessage({ type: 'requestViews' });
2502
- panelApi.postMessage({ type: 'requestPanelState', panelId: PANEL_ID });
2503
- panelApi.postMessage({ type: 'requestArtifacts' });
2504
- panelApi.postMessage({ type: 'requestSummary' });
2505
- panelApi.postMessage({ type: 'requestFilters' });
2506
- });
222
+ })();</script>`;return/<\/body>/i.test(e)?e.replace(/<\/body>/i,`${n}</body>`):`${e}${n}`}function requestHtmlPreviewContents(e){for(const t of e)t.format!=="html"||!t.localPath||panelApi.postMessage({type:"requestArtifactContent",path:t.localPath,format:"html"})}function requestMarkdownPreviewContents(e){for(const t of e)t.format!=="md"||!t.localPath||panelApi.postMessage({type:"requestArtifactContent",path:t.localPath,format:"md"})}function renderHtmlPreview(e,t,n){const r=Array.from(document.querySelectorAll(".artifact-card")).find(l=>l.dataset.path===e)?.querySelector(".html-preview");if(!r)return;if(n||!t){const l=n||"No preview content available.";r.innerHTML=`<div class="html-preview-fallback">Preview unavailable (${escapeHtml(l)}). Use <strong>Open</strong> to inspect the artifact.</div>`;return}const s=document.querySelector("script[nonce]")?.getAttribute("nonce")||"",o=document.createElement("iframe");o.setAttribute("sandbox","allow-scripts allow-same-origin"),o.setAttribute("allow","clipboard-read; clipboard-write"),o.setAttribute("loading","lazy"),o.tabIndex=0;const f=injectNonceIntoInlineScripts(t,s);o.srcdoc=injectIframeKeyboardShortcutBridge(f,s),o.addEventListener("load",()=>{r.classList.add("ready");const l=r.closest(".artifact-card");l instanceof HTMLElement&&l.classList.contains("fleet-dashboard-full")&&autoFitFleetCardHeight(l)}),r.innerHTML="",r.appendChild(o)}function initQuickPrompts(){document.querySelectorAll(".quick-prompt").forEach(e=>{const t=e;t.addEventListener("click",()=>{const n=t.dataset.prompt;n&&panelApi.postMessage({type:"openChatWithPrompt",prompt:n})})}),document.getElementById("ask-ai-btn")?.addEventListener("click",()=>{panelApi.postMessage({type:"openChat"})}),document.getElementById("refresh-charts-btn")?.addEventListener("click",()=>{refreshCurrentView()}),document.getElementById("expand-sidebar-btn")?.remove(),document.getElementById("shrink-sidebar-btn")?.remove(),document.getElementById("summary-toggle-btn")?.addEventListener("click",e=>{e.stopPropagation(),toggleSummarySection()}),applySummarySectionVisibility()}function applyFiltersToControls(){const e=document.getElementById("agent-filter-select");if(e){e.innerHTML='<option value="">All agents</option>';for(const t of state.agentIds){const n=document.createElement("option");n.value=t,n.textContent=t,e.appendChild(n)}e.value=state.agentId||""}}function postFilterUpdate(){panelApi.postMessage({type:"updateFilters",agentId:state.agentId||""})}function initFilters(){const e=document.getElementById("agent-filter-select");e?.addEventListener("change",()=>{state.agentId=e.value||"",postFilterUpdate()})}function initStateActions(){document.getElementById("cancel-generate-btn")?.addEventListener("click",()=>{panelApi.postMessage({type:"cancelGeneration"})}),document.getElementById("retry-generate-btn")?.addEventListener("click",()=>{panelApi.postMessage({type:"retryGeneration"})})}function initToolbarMenu(){const e=document.getElementById("toolbar-menu-btn"),t=document.getElementById("toolbar-menu-dropdown");if(!e||!t)return;function n(a){const r=a??!t.classList.contains("open");t.classList.toggle("open",r),t.setAttribute("aria-hidden",String(!r)),e.setAttribute("aria-expanded",String(r))}e.addEventListener("click",a=>{a.stopPropagation(),n()}),document.addEventListener("click",a=>{!t.contains(a.target)&&a.target!==e&&n(!1)}),document.addEventListener("keydown",a=>{a.key==="Escape"&&t.classList.contains("open")&&(n(!1),e.focus())}),t.querySelectorAll(".dropdown-item[data-action]").forEach(a=>{const r=a;r.addEventListener("click",()=>{const s=r.dataset.action;n(!1);const o=getActiveView();switch(s){case"new-view":promptCreateView();break;case"duplicate-view":o?.id&&panelApi.postMessage({type:"duplicateView",viewId:o.id});break;case"rename-view":{if(!o||o.system)break;panelApi.postMessage({type:"requestRenameView",viewId:o.id});break}case"delete-view":{if(!o||o.system)break;panelApi.postMessage({type:"requestDeleteView",viewId:o.id});break}case"edit-view-scripts":panelApi.postMessage({type:"editViewScripts",viewId:o?.id});break;case"share-scripts-only":onShareScriptsOnlyClick();break;case"refresh-all-views":panelApi.postMessage({type:"refreshAllViews"});break;case"reset-home":confirm("Reset all charts and scripts in your home analytics folder? This will delete generated charts and user scripts, then regenerate defaults.")&&panelApi.postMessage({type:"resetChartsHome"});break;case"reset-workspace":confirm("Reset all charts and scripts in this workspace? This will delete generated charts and user scripts for the current workspace, then regenerate defaults.")&&panelApi.postMessage({type:"resetChartsWorkspace"});break;case"reenable-charts":panelApi.postMessage({type:"reenableAllCharts"});break}})})}function postViewExportSnapshotToDashboardIframes(e){const t=e.payload,n=e.meta;document.querySelectorAll(".html-preview iframe").forEach(a=>{const r=a;try{r.contentWindow?.postMessage({type:VIEW_EXPORT_SNAPSHOT,payload:t,meta:n},"*"),r.contentWindow?.postMessage({type:LEGACY_VIEW_EXPORT_SNAPSHOT,payload:t,meta:n},"*")}catch{}})}function initEmbeddedWidgetBridge(){window.addEventListener("message",e=>{const t=e.data;if(!(!t||typeof t!="object")&&t.source==="agentide-wow-widget"){if(t.type==="copySessionId"&&typeof t.sessionId=="string"){const n=t.sessionId.trim();n&&panelApi.postMessage({type:"copySessionId",sessionId:n});return}if(t.type==="requestClipboardText"&&typeof t.requestId=="string"){const n=t.requestId.trim();n&&panelApi.postMessage({type:"requestClipboardText",requestId:n});return}if(t.type==="writeClipboardText"&&typeof t.text=="string"){panelApi.postMessage({type:"writeClipboardText",text:t.text});return}if(t.type==="openArtifact"&&typeof t.path=="string"){const n=t.path.trim();n&&panelApi.postMessage({type:"openArtifact",path:n});return}if(t.type==="openChatWithPrompt"&&typeof t.prompt=="string"){const n=t.prompt.trim();n&&panelApi.postMessage({type:"openChatWithPrompt",prompt:n.slice(0,12e3)});return}if(t.type==="disconnectFleetChannel"&&typeof t.sessionId=="string"){const n=t.sessionId.trim();n&&panelApi.postMessage({type:"disconnectFleetChannel",sessionId:n,channelType:typeof t.channelType=="string"?t.channelType:"",channelTarget:typeof t.channelTarget=="string"?t.channelTarget:""});return}if(t.type==="updateFleetCardTitle"&&typeof t.sessionId=="string"&&typeof t.cardTitle=="string"){const n=t.sessionId.trim(),a=typeof t.sourceId=="string"?t.sourceId.trim():"local";n&&panelApi.postMessage({type:"updateFleetCardTitle",sourceId:a,sessionId:n,cardTitle:t.cardTitle});return}if(t.type==="openSpotlightForSession"&&typeof t.sessionId=="string"){const n=t.sessionId.trim();n&&panelApi.postMessage({type:"openSpotlightForSession",sessionId:n,channelType:typeof t.channelType=="string"?t.channelType:"",channelTarget:typeof t.channelTarget=="string"?t.channelTarget:"",model:typeof t.model=="string"?t.model:"",engine:typeof t.engine=="string"?t.engine:"",agentId:typeof t.agentId=="string"?t.agentId:""});return}if(t.type==="refreshView"){panelApi.postMessage({type:"refreshView"});return}if(t.type==="refreshFleetWidget"){panelApi.postMessage({type:"refreshFleetWidget"});return}if(t.type==="refreshWidget"){const n=typeof t.script=="string"?t.script.trim():"",a=Array.isArray(t.scripts)?t.scripts.filter(r=>typeof r=="string").map(r=>r.trim()).filter(Boolean):[];if(!n&&a.length===0)return;panelApi.postMessage({type:"refreshWidget",script:n||void 0,scripts:a.length>0?a:void 0});return}if(t.type===REQUEST_VIEW_EXPORT_SNAPSHOT||t.type===LEGACY_REQUEST_LIVE_FLEET_SNAPSHOT){panelApi.postMessage({type:REQUEST_VIEW_EXPORT_SNAPSHOT});return}t.type==="openChat"&&panelApi.postMessage({type:"openChat"})}}),window.addEventListener("message",e=>{const t=e.data;if(!t||typeof t!="object"||t.type!=="openSpotlightForSession"||t.source==="agentide-wow-widget"||typeof t.sessionId!="string")return;const n=t.sessionId.trim();n&&panelApi.postMessage({type:"openSpotlightForSession",sessionId:n,channelType:typeof t.channelType=="string"?t.channelType:"",channelTarget:typeof t.channelTarget=="string"?t.channelTarget:"",model:typeof t.model=="string"?t.model:"",engine:typeof t.engine=="string"?t.engine:"",agentId:typeof t.agentId=="string"?t.agentId:""})})}function showGeneratingState(){showGalleryState("generating")}function showFailedState(e){const t=document.getElementById("error-message");t&&(t.textContent=e),showGalleryState("failed")}function showRefreshHint(){const e=document.getElementById("refresh-hint");e&&(e.style.display="flex")}function hideRefreshHint(){const e=document.getElementById("refresh-hint");e&&(e.style.display="none")}function setPerChartRefreshing(e){document.querySelectorAll('.artifact-card[data-type="chart"]').forEach(t=>{t.classList.toggle("chart-refreshing",e);let n=t.querySelector(".artifact-refresh-indicator");e?n||(n=document.createElement("div"),n.className="artifact-refresh-indicator",n.textContent="Refreshing",t.appendChild(n)):n&&n.remove()})}function setRefreshButtonState(e){const t=document.getElementById("refresh-charts-btn");t&&t.classList.toggle("refreshing",e)}panelApi.onMessage(e=>{switch(e.type){case"artifactsUpdated":{const t=getArtifactCatalogSignature(e.artifacts),n=typeof e.activeViewId=="string"?e.activeViewId:state.activeViewId,a=typeof e.fleetRefreshVersion=="number"?Math.max(0,Math.floor(e.fleetRefreshVersion)):state.fleetRefreshVersion,r=Array.isArray(e.galleryIgnorePatterns)?e.galleryIgnorePatterns.filter(c=>typeof c=="string").map(c=>String(c).trim()).filter(Boolean):[],s=n==="fleet",o=activeViewPreservesFleetIframe()&&t!==state.lastArtifactCatalogSignature&&getArtifactCatalogSignatureExcludingExports(e.artifacts)===getArtifactCatalogSignatureExcludingExports(state.artifacts),f=s&&a>state.fleetRefreshVersion&&!activeViewPreservesFleetIframe();if(t===state.lastArtifactCatalogSignature&&!state.isRefreshing&&!f)break;state.lastArtifactCatalogSignature=t,state.artifacts=e.artifacts,state.galleryIgnorePatterns=r;const l=state.activeViewId;if((n===state.activeViewId||!state.views.some(c=>c.id===state.activeViewId))&&(state.activeViewId=n),state.activeViewId!==l&&resetRefreshTrackingForViewChange(),state.fleetRefreshVersion=a,applyFleetAutoRefresh(),o)break;state.isRefreshing&&setPerChartRefreshing(!0),requestGalleryRender({forceFreshHtml:f});break}case"artifactDeleted":{const t=document.querySelector(`[data-path="${e.path}"]`);t&&(t.classList.add("deleting"),setTimeout(()=>t.remove(),300),state.artifacts=state.artifacts.filter(n=>n.localPath!==e.path),state.artifacts.length===0&&showGalleryState("empty"));break}case"summaryCards":state.summaryCards=e.cards,renderSummaryCards(e.cards);break;case"manifestUpdated":state.manifest=e.manifest,state.artifacts.length>0&&requestGalleryRender();break;case VIEW_EXPORT_SNAPSHOT:case LEGACY_VIEW_EXPORT_SNAPSHOT:postViewExportSnapshotToDashboardIframes(e);break;case"artifactContent":if(e.path&&e.format==="html"&&renderHtmlPreview(e.path,e.content,e.error),e.path&&e.format==="md"){const n=Array.from(document.querySelectorAll(".artifact-card")).find(a=>a.dataset.path===e.path)?.querySelector(".markdown-preview");n&&(e.error||!e.content?n.innerHTML="<em>Failed to load preview</em>":n.innerHTML=renderMarkdown(e.content))}break;case"clipboardText":document.querySelectorAll(".html-preview iframe").forEach(t=>{t.contentWindow?.postMessage({source:"agentide-panel",type:"clipboardText",requestId:e.requestId,text:typeof e.text=="string"?e.text:""},"*")});break;case"generatingStarted":setRefreshButtonState(!0),state.isRefreshing=!1,state.refreshingViewId=null,state.needsGalleryRender=!1,state.pendingForceFreshHtml=!1,setPerChartRefreshing(!1),showGeneratingState();break;case"generatingCancelled":setRefreshButtonState(!1),state.isRefreshing=!1,state.refreshingViewId=null,state.needsGalleryRender=!1,state.pendingForceFreshHtml=!1,setPerChartRefreshing(!1),state.artifacts.length>0?renderGallery(state.artifacts):showGalleryState("empty");break;case"generatingFailed":setRefreshButtonState(!1),state.isRefreshing=!1,state.refreshingViewId=null,state.needsGalleryRender=!1,state.pendingForceFreshHtml=!1,setPerChartRefreshing(!1),showFailedState(e.error);break;case"refreshStarted":(!e.viewId||e.viewId===state.activeViewId)&&(setRefreshButtonState(!0),state.isRefreshing=!0,state.refreshingViewId=typeof e.viewId=="string"&&e.viewId?e.viewId:null,setPerChartRefreshing(!0));break;case"refreshFinished":(!e.viewId||e.viewId===state.activeViewId||e.viewId===state.refreshingViewId)&&(setRefreshButtonState(!1),state.isRefreshing=!1,state.refreshingViewId=null,setPerChartRefreshing(!1),flushPendingGalleryRender(),activeViewPreservesFleetIframe()&&document.querySelectorAll(".html-preview iframe").forEach(t=>{const n=t;try{n.contentWindow?.postMessage({type:"fleetRefreshComplete"},"*")}catch{}}));break;case"viewsLoaded":{(e.surfaceMode==="team"||e.surfaceMode==="spotlight")&&applySurfaceMode(e.surfaceMode);const t=state.activeViewId;state.views=Array.isArray(e.views)?e.views:[],state.activeViewId=typeof e.activeViewId=="string"?e.activeViewId:state.activeViewId,state.activeViewId!==t&&resetRefreshTrackingForViewChange(),applyViewOptions(),applyFleetAutoRefresh();break}case"refreshNoData":(!e.viewId||e.viewId===state.activeViewId||e.viewId===state.refreshingViewId)&&(setRefreshButtonState(!1),state.isRefreshing=!1,state.refreshingViewId=null,setPerChartRefreshing(!1),flushPendingGalleryRender());break;case"refreshFailed":(!e.viewId||e.viewId===state.activeViewId||e.viewId===state.refreshingViewId)&&(setRefreshButtonState(!1),state.isRefreshing=!1,state.refreshingViewId=null,setPerChartRefreshing(!1),flushPendingGalleryRender(),state.artifacts.length===0&&showFailedState(e.error));break;case"filtersUpdated":state.scope=e.scope==="workspace"?"workspace":"global",state.projectId=e.projectId||"",state.agentId=e.agentId||"",state.agentIds=Array.isArray(e.agentIds)?e.agentIds:[],applyViewOptions(),applyFleetAutoRefresh(),applyFiltersToControls();break;case"panelStateLoaded":if(e.panelId&&e.panelId!==PANEL_ID)break;(e.surfaceMode==="team"||e.surfaceMode==="spotlight")&&applySurfaceMode(e.surfaceMode),state.panelState=normalizePanelState(e.state),state.panelStateLoaded=!0,applySummarySectionVisibility(),typeof e.fleetAutoRefreshIntervalSeconds=="number"&&(state.fleetAutoRefreshIntervalSeconds=Math.max(0,e.fleetAutoRefreshIntervalSeconds)),applyFleetAutoRefresh(),ensurePanelStateForArtifacts(state.artifacts)&&savePanelState(),state.artifacts.length>0&&renderGallery(state.artifacts);break;case"disabledChartsUpdated":state.disabledCharts=new Set((Array.isArray(e.charts)?e.charts:[]).map(t=>String(t||"").trim().toLowerCase()).filter(Boolean)),requestGalleryRender();break;case"dataStale":showRefreshHint();break;case"dataFresh":hideRefreshHint();break}}),document.addEventListener("DOMContentLoaded",()=>{document.body.classList.contains("spotlight-surface-team")&&applySurfaceMode("team"),ensureShareToolbarButton(),initQuickPrompts(),initStateActions(),initEmbeddedWidgetBridge(),initFilters(),initViews(),initToolbarMenu(),renderSummaryPlaceholders(),panelApi.postMessage({type:"requestViews"}),panelApi.postMessage({type:"requestPanelState",panelId:PANEL_ID}),panelApi.postMessage({type:"requestArtifacts"}),panelApi.postMessage({type:"requestSummary"}),panelApi.postMessage({type:"requestFilters"})});