wao 0.41.3 → 0.41.5

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 (719) hide show
  1. package/.babelrc-cjs +5 -0
  2. package/.babelrc-esm +5 -0
  3. package/.gitmodules +4 -0
  4. package/.prettierrc +6 -0
  5. package/README.md +90 -0
  6. package/app/.eslintrc.json +3 -0
  7. package/app/.mocharc.cjs +6 -0
  8. package/app/arweave.mjs +87 -0
  9. package/app/components/Footer.js +76 -0
  10. package/app/components/Global.js +1128 -0
  11. package/app/components/Header.js +306 -0
  12. package/app/components/Left.js +28 -0
  13. package/app/components/Logo.js +34 -0
  14. package/app/components/Middle.js +37 -0
  15. package/app/components/Sidebar.js +93 -0
  16. package/app/components/Tags.js +42 -0
  17. package/app/components/editor/Editor.js +322 -0
  18. package/app/components/editor/FilePath.js +42 -0
  19. package/app/components/editor/MonacoEditor.js +44 -0
  20. package/app/components/left/LeftMessages.js +85 -0
  21. package/app/components/left/LeftModules.js +86 -0
  22. package/app/components/left/LeftNetworks.js +438 -0
  23. package/app/components/left/LeftProcesses.js +98 -0
  24. package/app/components/left/LeftProjects.js +397 -0
  25. package/app/components/left/LeftTests.js +189 -0
  26. package/app/components/middle/MiddleBlocks.js +79 -0
  27. package/app/components/middle/MiddleEntity.js +16 -0
  28. package/app/components/middle/MiddleEntityAccount.js +264 -0
  29. package/app/components/middle/MiddleEntityAssignment.js +104 -0
  30. package/app/components/middle/MiddleEntityBlock.js +148 -0
  31. package/app/components/middle/MiddleEntityMessage.js +430 -0
  32. package/app/components/middle/MiddleEntityModule.js +183 -0
  33. package/app/components/middle/MiddleEntityProcess.js +323 -0
  34. package/app/components/middle/MiddleEntityTx.js +211 -0
  35. package/app/components/middle/MiddleMessages.js +87 -0
  36. package/app/components/middle/MiddleModules.js +89 -0
  37. package/app/components/middle/MiddleNetworks.js +516 -0
  38. package/app/components/middle/MiddleProcesses.js +87 -0
  39. package/app/components/middle/MiddleTests.js +134 -0
  40. package/app/components/modals/CreateFileModal.js +115 -0
  41. package/app/components/modals/CreateFolderModal.js +83 -0
  42. package/app/components/modals/CreateProjectModal.js +63 -0
  43. package/app/components/modals/FSModal.js +141 -0
  44. package/app/components/modals/ImportModal.js +105 -0
  45. package/app/components/modals/LaunchNetworkModal.js +95 -0
  46. package/app/components/modals/Modal.js +38 -0
  47. package/app/components/modals/ProxyModal.js +122 -0
  48. package/app/components/modals/RenameFileModal.js +95 -0
  49. package/app/components/styles/EditorScrollbarStyle.js +29 -0
  50. package/app/components/styles/EntityScrollbarStyle.js +29 -0
  51. package/app/components/styles/GlobalStyle.js +102 -0
  52. package/app/components/styles/XtermStyle.js +37 -0
  53. package/app/components/terminal/Terminal.js +132 -0
  54. package/app/components/terminal/Xterm.js +160 -0
  55. package/app/components/ui/avatar.jsx +49 -0
  56. package/app/components/ui/checkbox.jsx +17 -0
  57. package/app/components/ui/close-button.jsx +20 -0
  58. package/app/components/ui/color-mode.jsx +61 -0
  59. package/app/components/ui/dialog.jsx +54 -0
  60. package/app/components/ui/drawer.jsx +44 -0
  61. package/app/components/ui/field.jsx +22 -0
  62. package/app/components/ui/input-group.jsx +39 -0
  63. package/app/components/ui/popover.jsx +49 -0
  64. package/app/components/ui/provider.jsx +12 -0
  65. package/app/components/ui/radio.jsx +17 -0
  66. package/app/components/ui/slider.jsx +107 -0
  67. package/app/components/ui/toaster.jsx +69 -0
  68. package/app/components/ui/tooltip.jsx +35 -0
  69. package/app/index.js +10 -0
  70. package/app/jsconfig.json +7 -0
  71. package/app/lib/GithubCSS.js +1218 -0
  72. package/app/lib/addon-fit.js +72 -0
  73. package/app/lib/data.js +31 -0
  74. package/app/lib/github-markdown.css +1169 -0
  75. package/app/lib/global.js +1 -0
  76. package/app/lib/guide.js +82 -0
  77. package/app/lib/hub.js +109 -0
  78. package/app/lib/scripts.js +47 -0
  79. package/app/lib/store.js +34 -0
  80. package/app/lib/use.js +64 -0
  81. package/app/lib/utils.js +387 -0
  82. package/app/lib/webrtc.js +217 -0
  83. package/app/next.config.js +25 -0
  84. package/app/package.json +62 -0
  85. package/app/pages/404.js +9 -0
  86. package/app/pages/_app.js +10 -0
  87. package/app/pages/_document.js +14 -0
  88. package/app/pages/index.js +121 -0
  89. package/app/playwright.config.mjs +41 -0
  90. package/app/proxy.mjs +21 -0
  91. package/app/public/assets/cover.png +0 -0
  92. package/app/public/blueprints/apm.lua +91 -0
  93. package/app/public/blueprints/arena.lua +345 -0
  94. package/app/public/blueprints/arns.lua +318 -0
  95. package/app/public/blueprints/chat.lua +210 -0
  96. package/app/public/blueprints/chatroom.lua +56 -0
  97. package/app/public/blueprints/patch-legacy-reply.lua +13 -0
  98. package/app/public/blueprints/staking.lua +93 -0
  99. package/app/public/blueprints/token.lua +298 -0
  100. package/app/public/blueprints/voting.lua +45 -0
  101. package/app/public/favicon.ico +0 -0
  102. package/app/public/logo.png +0 -0
  103. package/cli-esm.js +55 -0
  104. package/cli.js +54 -0
  105. package/devnet/app/api.js +129 -0
  106. package/devnet/app/components/layout.js +89 -0
  107. package/devnet/app/components/pagination.js +144 -0
  108. package/devnet/app/components/search.js +159 -0
  109. package/devnet/app/components/table.js +124 -0
  110. package/devnet/app/components/tags.js +89 -0
  111. package/devnet/app/index.html +14 -0
  112. package/devnet/app/main.js +215 -0
  113. package/devnet/app/router.js +59 -0
  114. package/devnet/app/state.js +19 -0
  115. package/devnet/app/style.css +1606 -0
  116. package/devnet/app/utils.js +77 -0
  117. package/devnet/app/views/blocks.js +75 -0
  118. package/devnet/app/views/dashboard.js +127 -0
  119. package/devnet/app/views/entity.js +1473 -0
  120. package/devnet/app/views/messages.js +90 -0
  121. package/devnet/app/views/modules.js +165 -0
  122. package/devnet/app/views/processes.js +90 -0
  123. package/devnet/app/views/schedulers.js +90 -0
  124. package/devnet/app/views/transactions.js +88 -0
  125. package/devnet/migrations/0001_initial.sql +51 -0
  126. package/devnet/package.json +26 -0
  127. package/devnet/playwright.config.js +7 -0
  128. package/devnet/src/cu-worker.js +166 -0
  129. package/devnet/src/index.js +309 -0
  130. package/devnet/vite.config.js +17 -0
  131. package/devnet/vitest.config.js +10 -0
  132. package/devnet/wrangler-cu.toml +32 -0
  133. package/devnet/wrangler.toml +38 -0
  134. package/dhfs-tutorial-app/package.json +15 -0
  135. package/dist/esm/workspace/dashboard/public/favicon.png +0 -0
  136. package/dist/esm/workspace/src/counter.lua +14 -0
  137. package/dist/package.json +82 -0
  138. package/hb.sh +18 -0
  139. package/hbsig/.babelrc-cjs +5 -0
  140. package/hbsig/.babelrc-esm +5 -0
  141. package/hbsig/README.md +1 -0
  142. package/hbsig/dist/cjs/bin_to_str.js +44 -0
  143. package/hbsig/dist/cjs/collect-body-keys.js +470 -0
  144. package/hbsig/dist/cjs/commit.js +330 -0
  145. package/hbsig/dist/cjs/encode-array-item.js +110 -0
  146. package/hbsig/dist/cjs/encode-utils.js +242 -0
  147. package/hbsig/dist/cjs/encode.js +1365 -0
  148. package/hbsig/dist/cjs/erl_json.js +326 -0
  149. package/hbsig/dist/cjs/erl_str.js +1042 -0
  150. package/hbsig/dist/cjs/flat.js +279 -0
  151. package/hbsig/dist/cjs/http-message-signatures/httpbis.js +489 -0
  152. package/hbsig/dist/cjs/http-message-signatures/index.js +25 -0
  153. package/hbsig/dist/cjs/http-message-signatures/structured-header.js +129 -0
  154. package/hbsig/dist/cjs/httpsig.js +861 -0
  155. package/hbsig/dist/cjs/id.js +470 -0
  156. package/hbsig/dist/cjs/index.js +174 -0
  157. package/hbsig/dist/cjs/nocrypto.js +51 -0
  158. package/hbsig/dist/cjs/parser.js +201 -0
  159. package/hbsig/dist/cjs/send-utils.js +1231 -0
  160. package/hbsig/dist/cjs/send.js +211 -0
  161. package/hbsig/dist/cjs/signer-utils.js +494 -0
  162. package/hbsig/dist/cjs/signer.js +436 -0
  163. package/hbsig/dist/cjs/structured.js +515 -0
  164. package/hbsig/dist/cjs/test.js +12 -0
  165. package/hbsig/dist/cjs/utils.js +44 -0
  166. package/hbsig/dist/esm/bin_to_str.js +46 -0
  167. package/hbsig/dist/esm/collect-body-keys.js +436 -0
  168. package/hbsig/dist/esm/commit.js +219 -0
  169. package/hbsig/dist/esm/encode-array-item.js +112 -0
  170. package/hbsig/dist/esm/encode-utils.js +191 -0
  171. package/hbsig/dist/esm/encode.js +1256 -0
  172. package/hbsig/dist/esm/erl_json.js +292 -0
  173. package/hbsig/dist/esm/erl_str.js +1144 -0
  174. package/hbsig/dist/esm/flat.js +250 -0
  175. package/hbsig/dist/esm/http-message-signatures/httpbis.js +438 -0
  176. package/hbsig/dist/esm/http-message-signatures/index.js +4 -0
  177. package/hbsig/dist/esm/http-message-signatures/structured-header.js +105 -0
  178. package/hbsig/dist/esm/httpsig.js +866 -0
  179. package/hbsig/dist/esm/id.js +459 -0
  180. package/hbsig/dist/esm/index.js +13 -0
  181. package/hbsig/dist/esm/nocrypto.js +4 -0
  182. package/hbsig/dist/esm/package.json +3 -0
  183. package/hbsig/dist/esm/parser.js +171 -0
  184. package/hbsig/dist/esm/send-utils.js +1132 -0
  185. package/hbsig/dist/esm/send.js +142 -0
  186. package/hbsig/dist/esm/signer-utils.js +375 -0
  187. package/hbsig/dist/esm/signer.js +312 -0
  188. package/hbsig/dist/esm/structured.js +496 -0
  189. package/hbsig/dist/esm/test.js +2 -0
  190. package/hbsig/dist/esm/utils.js +29 -0
  191. package/hbsig/dist/package.json +39 -0
  192. package/hbsig/make.js +36 -0
  193. package/hbsig/package.json +39 -0
  194. package/hbsig/src/bin_to_str.js +46 -0
  195. package/hbsig/src/collect-body-keys.js +436 -0
  196. package/hbsig/src/commit.js +219 -0
  197. package/hbsig/src/encode-array-item.js +112 -0
  198. package/hbsig/src/encode-utils.js +191 -0
  199. package/hbsig/src/encode.js +1256 -0
  200. package/hbsig/src/erl_json.js +292 -0
  201. package/hbsig/src/erl_str.js +1144 -0
  202. package/hbsig/src/flat.js +250 -0
  203. package/hbsig/src/http-message-signatures/httpbis.js +438 -0
  204. package/hbsig/src/http-message-signatures/index.js +4 -0
  205. package/hbsig/src/http-message-signatures/structured-header.js +105 -0
  206. package/hbsig/src/httpsig.js +866 -0
  207. package/hbsig/src/id.js +459 -0
  208. package/hbsig/src/index.js +13 -0
  209. package/hbsig/src/nocrypto.js +4 -0
  210. package/hbsig/src/parser.js +171 -0
  211. package/hbsig/src/send-utils.js +1132 -0
  212. package/hbsig/src/send.js +142 -0
  213. package/hbsig/src/signer-utils.js +375 -0
  214. package/hbsig/src/signer.js +312 -0
  215. package/hbsig/src/structured.js +496 -0
  216. package/hbsig/src/test.js +2 -0
  217. package/hbsig/src/utils.js +29 -0
  218. package/lp/index.html +25 -0
  219. package/lp/package.json +19 -0
  220. package/lp/public/cover.png +0 -0
  221. package/lp/public/favicon.ico +0 -0
  222. package/lp/public/favicon.png +0 -0
  223. package/lp/public/images/book.jpg +0 -0
  224. package/lp/public/images/dashboard.png +0 -0
  225. package/lp/public/images/devnet.png +0 -0
  226. package/lp/public/images/hero-bg.png +0 -0
  227. package/lp/public/images/hyperadd.png +0 -0
  228. package/lp/public/images/message-flow.png +0 -0
  229. package/lp/public/images/mobile.png +0 -0
  230. package/lp/public/images/og-cover.png +0 -0
  231. package/lp/public/images/scan.png +0 -0
  232. package/lp/public/images/wao-hub.png +0 -0
  233. package/lp/public/images/wao-web.png +0 -0
  234. package/lp/public/logo.png +0 -0
  235. package/lp/src/App.jsx +29 -0
  236. package/lp/src/components/CodeBlock.jsx +37 -0
  237. package/lp/src/components/CodeBlock.module.css +89 -0
  238. package/lp/src/components/Footer.jsx +22 -0
  239. package/lp/src/components/Footer.module.css +60 -0
  240. package/lp/src/components/Nav.jsx +80 -0
  241. package/lp/src/components/Nav.module.css +177 -0
  242. package/lp/src/components/SectionWrapper.jsx +15 -0
  243. package/lp/src/hooks/useReveal.js +25 -0
  244. package/lp/src/hooks/useScrolled.js +14 -0
  245. package/lp/src/main.jsx +10 -0
  246. package/lp/src/sections/Book.jsx +60 -0
  247. package/lp/src/sections/Book.module.css +204 -0
  248. package/lp/src/sections/Browser.jsx +46 -0
  249. package/lp/src/sections/Browser.module.css +67 -0
  250. package/lp/src/sections/Devnet.jsx +55 -0
  251. package/lp/src/sections/Devnet.module.css +136 -0
  252. package/lp/src/sections/GetStarted.jsx +51 -0
  253. package/lp/src/sections/GetStarted.module.css +128 -0
  254. package/lp/src/sections/Hero.jsx +67 -0
  255. package/lp/src/sections/Hero.module.css +202 -0
  256. package/lp/src/sections/HyperADD.jsx +46 -0
  257. package/lp/src/sections/HyperADD.module.css +113 -0
  258. package/lp/src/sections/SDK.jsx +90 -0
  259. package/lp/src/sections/SDK.module.css +122 -0
  260. package/lp/src/sections/Testing.jsx +47 -0
  261. package/lp/src/sections/Testing.module.css +124 -0
  262. package/lp/src/sections/Vision.jsx +93 -0
  263. package/lp/src/sections/Vision.module.css +147 -0
  264. package/lp/src/styles/globals.css +285 -0
  265. package/lp/src/utils/scrollTo.js +4 -0
  266. package/lp/vite.config.js +7 -0
  267. package/make.js +39 -0
  268. package/package.json +17 -12
  269. package/patches/@permaweb+ao-loader+0.0.44.patch +64 -0
  270. package/plan.md +435 -0
  271. package/rebar.lock +1 -0
  272. package/src/accounts-web.js +128 -0
  273. package/src/accounts.js +124 -0
  274. package/src/adaptor-base.js +622 -0
  275. package/src/adaptor-cf.js +11 -0
  276. package/src/adaptor-web.js +11 -0
  277. package/src/adaptor.js +10 -0
  278. package/src/ao-loader.js +30660 -0
  279. package/src/ao.js +1173 -0
  280. package/src/aoconnect-base.js +1098 -0
  281. package/src/aoconnect-cf.js +9 -0
  282. package/src/aoconnect-web.js +8 -0
  283. package/src/aoconnect.js +8 -0
  284. package/src/aos_wamr.js +1 -0
  285. package/src/ar-remote.js +87 -0
  286. package/src/ar.js +276 -0
  287. package/src/armem-base.js +529 -0
  288. package/src/armem-cf.js +67 -0
  289. package/src/armem-web.js +19 -0
  290. package/src/armem.js +26 -0
  291. package/src/bao.js +141 -0
  292. package/src/bar.js +366 -0
  293. package/src/build.js +7 -0
  294. package/src/bundler.js +119 -0
  295. package/src/car.js +10 -0
  296. package/src/cf-env.js +29 -0
  297. package/src/cf.js +11 -0
  298. package/src/cli.js +561 -0
  299. package/src/compress.js +112 -0
  300. package/src/create.js +415 -0
  301. package/src/devs.js +86 -0
  302. package/src/dirname.js +1 -0
  303. package/src/dodb.js +26 -0
  304. package/src/erl_json.js +289 -0
  305. package/src/erl_str.js +1139 -0
  306. package/src/gql.js +229 -0
  307. package/src/hb.js +967 -0
  308. package/src/helpers.js +203 -0
  309. package/src/http.js +619 -0
  310. package/src/hub/bundler.js +123 -0
  311. package/src/hub/cu.js +74 -0
  312. package/src/hub/fs.js +157 -0
  313. package/src/hub/index.js +85 -0
  314. package/src/hub/utils.js +64 -0
  315. package/src/hub/ws-proxy.js +67 -0
  316. package/src/hyper-aos.js +1 -0
  317. package/src/hyperbeam-server.js +21 -0
  318. package/src/hyperbeam.js +585 -0
  319. package/src/index.js +5 -0
  320. package/src/keygen.js +47 -0
  321. package/src/lfdb.js +67 -0
  322. package/src/lua/aos.wasm +0 -0
  323. package/src/lua/aos2_0_1.js +1 -0
  324. package/src/lua/aos2_0_1.wasm +0 -0
  325. package/src/lua/aos2_0_3.js +1 -0
  326. package/src/lua/aos2_0_3.wasm +0 -0
  327. package/src/lua/aos2_0_4_32.js +1 -0
  328. package/src/lua/aos2_0_4_32.wasm +0 -0
  329. package/src/lua/aos2_0_6.wasm +0 -0
  330. package/src/lua/apm.lua +61 -0
  331. package/src/lua/llama.wasm +0 -0
  332. package/src/lua/process.wasm +0 -0
  333. package/src/lua/sqlite.js +1 -0
  334. package/src/lua/sqlite.wasm +0 -0
  335. package/src/lua/weavedb_mock.lua +44 -0
  336. package/src/lua/weavedrive.lua +53 -0
  337. package/src/run.js +25 -0
  338. package/src/server.js +173 -0
  339. package/src/storage-multi.js +183 -0
  340. package/src/tao.js +12 -0
  341. package/src/tar.js +10 -0
  342. package/src/test.js +42 -0
  343. package/src/tgql-d1.js +407 -0
  344. package/src/tgql.js +310 -0
  345. package/src/toerl.js +162 -0
  346. package/src/utils.js +777 -0
  347. package/src/wao.js +29 -0
  348. package/src/waosm/README.md +81 -0
  349. package/src/waosm/waosm.d.ts +40 -0
  350. package/src/waosm/waosm.js +240 -0
  351. package/src/waosm/waosm_bg.wasm +0 -0
  352. package/src/waosm/waosm_bg.wasm.d.ts +8 -0
  353. package/src/waosm-node/README.md +81 -0
  354. package/src/waosm-node/waosm.d.ts +8 -0
  355. package/src/waosm-node/waosm.js +4 -0
  356. package/src/waosm-node/waosm_bg.js +147 -0
  357. package/src/waosm-node/waosm_bg.wasm +0 -0
  358. package/src/waosm-node/waosm_bg.wasm.d.ts +8 -0
  359. package/src/waosm-node/waosm_bg.wasm.js +14 -0
  360. package/src/waosm-node.js +9 -0
  361. package/src/war.js +10 -0
  362. package/src/weavedrive.js +376 -0
  363. package/src/web.js +11 -0
  364. package/src/workspace/.mcp.json +8 -0
  365. package/src/workspace/CLAUDE.md +258 -0
  366. package/src/workspace/README.md +85 -0
  367. package/src/workspace/dashboard/index.html +163 -0
  368. package/src/workspace/dashboard/package.json +18 -0
  369. package/src/workspace/dashboard/public/favicon.ico +0 -0
  370. package/src/workspace/dashboard/public/favicon.png +0 -0
  371. package/src/workspace/dashboard/server.js +224 -0
  372. package/src/workspace/dashboard/src/App.jsx +2350 -0
  373. package/{cjs/workspace/frontend → src/workspace/dashboard}/vite.config.js +6 -0
  374. package/src/workspace/package.json +18 -0
  375. package/src/workspace/src/counter.lua +14 -0
  376. package/tsconfig.json +18 -0
  377. package/types/index.d.ts +329 -0
  378. package/waollama/.eslintrc.json +3 -0
  379. package/waollama/README.md +19 -0
  380. package/waollama/arweave.mjs +87 -0
  381. package/waollama/components/ui/avatar.jsx +49 -0
  382. package/waollama/components/ui/checkbox.jsx +17 -0
  383. package/waollama/components/ui/close-button.jsx +20 -0
  384. package/waollama/components/ui/color-mode.jsx +61 -0
  385. package/waollama/components/ui/dialog.jsx +54 -0
  386. package/waollama/components/ui/drawer.jsx +44 -0
  387. package/waollama/components/ui/field.jsx +22 -0
  388. package/waollama/components/ui/input-group.jsx +39 -0
  389. package/waollama/components/ui/popover.jsx +49 -0
  390. package/waollama/components/ui/provider.jsx +12 -0
  391. package/waollama/components/ui/radio.jsx +17 -0
  392. package/waollama/components/ui/slider.jsx +107 -0
  393. package/waollama/components/ui/tooltip.jsx +35 -0
  394. package/waollama/index.js +10 -0
  395. package/waollama/jsconfig.json +7 -0
  396. package/waollama/lib/ao-loader.js +30654 -0
  397. package/waollama/lib/client.js +1 -0
  398. package/waollama/lib/wasm.js +1 -0
  399. package/waollama/next.config.js +25 -0
  400. package/waollama/package.json +43 -0
  401. package/waollama/pages/404.js +9 -0
  402. package/waollama/pages/_app.js +9 -0
  403. package/waollama/pages/_document.js +14 -0
  404. package/waollama/pages/index.js +212 -0
  405. package/waollama/playwright.config.js +31 -0
  406. package/waollama/proxy.mjs +21 -0
  407. package/waollama/public/favicon.ico +0 -0
  408. package/waollama/public/human.png +0 -0
  409. package/waollama/public/llama.png +0 -0
  410. package/waosm/Cargo.toml +11 -0
  411. package/waosm/README.md +81 -0
  412. package/waosm/compress.js +112 -0
  413. package/waosm/make.js +33 -0
  414. package/waosm/package.json +10 -0
  415. package/waosm/src/lib.rs +180 -0
  416. package/waosm/test.js +16 -0
  417. package/cjs/workspace/.claude/agents/builder.md +0 -99
  418. package/cjs/workspace/.claude/agents/device-builder.md +0 -86
  419. package/cjs/workspace/.claude/agents/tester.md +0 -90
  420. package/cjs/workspace/.claude/mcp/dashboard/server.js +0 -128
  421. package/cjs/workspace/.claude/rules/deploy.md +0 -45
  422. package/cjs/workspace/.claude/rules/frontend.md +0 -69
  423. package/cjs/workspace/.claude/rules/hyperbeam.md +0 -104
  424. package/cjs/workspace/.claude/rules/lua.md +0 -108
  425. package/cjs/workspace/.claude/rules/testing.md +0 -257
  426. package/cjs/workspace/.claude/settings.json +0 -109
  427. package/cjs/workspace/.claude/skills/build/SKILL.md +0 -214
  428. package/cjs/workspace/.claude/skills/build-aos/SKILL.md +0 -129
  429. package/cjs/workspace/.claude/skills/build-device/SKILL.md +0 -192
  430. package/cjs/workspace/.claude/skills/build-frontend/SKILL.md +0 -134
  431. package/cjs/workspace/.claude/skills/build-module/SKILL.md +0 -281
  432. package/cjs/workspace/.claude/skills/create-aos/SKILL.md +0 -63
  433. package/cjs/workspace/.claude/skills/create-device/SKILL.md +0 -87
  434. package/cjs/workspace/.claude/skills/create-module/SKILL.md +0 -162
  435. package/cjs/workspace/.claude/skills/debug/SKILL.md +0 -57
  436. package/cjs/workspace/.claude/skills/deploy/SKILL.md +0 -85
  437. package/cjs/workspace/.claude/skills/dev/SKILL.md +0 -32
  438. package/cjs/workspace/.claude/skills/plan/SKILL.md +0 -176
  439. package/cjs/workspace/.claude/skills/readme/SKILL.md +0 -186
  440. package/cjs/workspace/.claude/skills/report/SKILL.md +0 -49
  441. package/cjs/workspace/.claude/skills/team/SKILL.md +0 -101
  442. package/cjs/workspace/.claude/skills/test/SKILL.md +0 -56
  443. package/cjs/workspace/.claude/skills/test-device/SKILL.md +0 -114
  444. package/cjs/workspace/.claude/skills/test-e2e/SKILL.md +0 -103
  445. package/cjs/workspace/.claude/skills/test-hb/SKILL.md +0 -76
  446. package/cjs/workspace/.claude/skills/validate/SKILL.md +0 -96
  447. package/cjs/workspace/.claude/skills/validate/scripts/check-lua.sh +0 -41
  448. package/cjs/workspace/docs/aos-lua.md +0 -428
  449. package/cjs/workspace/docs/debug.md +0 -230
  450. package/cjs/workspace/docs/hyperbeam-dev.md +0 -606
  451. package/cjs/workspace/docs/hyperbeam-devices.md +0 -618
  452. package/cjs/workspace/docs/wao-sdk.md +0 -807
  453. package/cjs/workspace/frontend/e2e/.gitkeep +0 -0
  454. package/cjs/workspace/frontend/index.html +0 -12
  455. package/cjs/workspace/frontend/package.json +0 -24
  456. package/cjs/workspace/frontend/playwright.config.js +0 -10
  457. package/cjs/workspace/frontend/src/App.jsx +0 -24
  458. package/cjs/workspace/frontend/test/.gitkeep +0 -0
  459. package/cjs/workspace/frontend/vitest.config.js +0 -10
  460. package/cjs/workspace/scripts/deploy.js +0 -140
  461. package/cjs/workspace/scripts/keygen.js +0 -52
  462. package/cjs/workspace/test/aos.test.js +0 -98
  463. package/cjs/workspace/test/hyperbeam.test.js +0 -80
  464. package/esm/compiler/native +0 -0
  465. package/esm/workspace/.claude/agents/builder.md +0 -99
  466. package/esm/workspace/.claude/agents/device-builder.md +0 -86
  467. package/esm/workspace/.claude/agents/tester.md +0 -90
  468. package/esm/workspace/.claude/mcp/dashboard/server.js +0 -128
  469. package/esm/workspace/.claude/rules/deploy.md +0 -45
  470. package/esm/workspace/.claude/rules/frontend.md +0 -69
  471. package/esm/workspace/.claude/rules/hyperbeam.md +0 -104
  472. package/esm/workspace/.claude/rules/lua.md +0 -108
  473. package/esm/workspace/.claude/rules/testing.md +0 -257
  474. package/esm/workspace/.claude/settings.json +0 -109
  475. package/esm/workspace/.claude/skills/build/SKILL.md +0 -214
  476. package/esm/workspace/.claude/skills/build-aos/SKILL.md +0 -129
  477. package/esm/workspace/.claude/skills/build-device/SKILL.md +0 -192
  478. package/esm/workspace/.claude/skills/build-frontend/SKILL.md +0 -134
  479. package/esm/workspace/.claude/skills/build-module/SKILL.md +0 -281
  480. package/esm/workspace/.claude/skills/create-aos/SKILL.md +0 -63
  481. package/esm/workspace/.claude/skills/create-device/SKILL.md +0 -87
  482. package/esm/workspace/.claude/skills/create-module/SKILL.md +0 -162
  483. package/esm/workspace/.claude/skills/debug/SKILL.md +0 -57
  484. package/esm/workspace/.claude/skills/deploy/SKILL.md +0 -85
  485. package/esm/workspace/.claude/skills/dev/SKILL.md +0 -32
  486. package/esm/workspace/.claude/skills/plan/SKILL.md +0 -176
  487. package/esm/workspace/.claude/skills/readme/SKILL.md +0 -186
  488. package/esm/workspace/.claude/skills/report/SKILL.md +0 -49
  489. package/esm/workspace/.claude/skills/team/SKILL.md +0 -101
  490. package/esm/workspace/.claude/skills/test/SKILL.md +0 -56
  491. package/esm/workspace/.claude/skills/test-device/SKILL.md +0 -114
  492. package/esm/workspace/.claude/skills/test-e2e/SKILL.md +0 -103
  493. package/esm/workspace/.claude/skills/test-hb/SKILL.md +0 -76
  494. package/esm/workspace/.claude/skills/validate/SKILL.md +0 -96
  495. package/esm/workspace/.claude/skills/validate/scripts/check-lua.sh +0 -41
  496. package/esm/workspace/docs/aos-lua.md +0 -428
  497. package/esm/workspace/docs/debug.md +0 -230
  498. package/esm/workspace/docs/hyperbeam-dev.md +0 -606
  499. package/esm/workspace/docs/hyperbeam-devices.md +0 -618
  500. package/esm/workspace/docs/wao-sdk.md +0 -807
  501. package/esm/workspace/frontend/e2e/.gitkeep +0 -0
  502. package/esm/workspace/frontend/index.html +0 -12
  503. package/esm/workspace/frontend/package.json +0 -24
  504. package/esm/workspace/frontend/playwright.config.js +0 -10
  505. package/esm/workspace/frontend/src/App.jsx +0 -24
  506. package/esm/workspace/frontend/src/main.jsx +0 -9
  507. package/esm/workspace/frontend/test/.gitkeep +0 -0
  508. package/esm/workspace/frontend/vite.config.js +0 -6
  509. package/esm/workspace/frontend/vitest.config.js +0 -10
  510. package/esm/workspace/scripts/deploy.js +0 -140
  511. package/esm/workspace/scripts/keygen.js +0 -52
  512. package/esm/workspace/test/aos.test.js +0 -98
  513. package/esm/workspace/test/hyperbeam.test.js +0 -80
  514. /package/{cjs/workspace/dashboard/public → devnet/app}/favicon.ico +0 -0
  515. /package/{cjs/workspace → dhfs-tutorial-app}/src/counter.lua +0 -0
  516. /package/{cjs → dist/cjs}/accounts-web.js +0 -0
  517. /package/{cjs → dist/cjs}/accounts.js +0 -0
  518. /package/{cjs → dist/cjs}/adaptor-base.js +0 -0
  519. /package/{cjs → dist/cjs}/adaptor-cf.js +0 -0
  520. /package/{cjs → dist/cjs}/adaptor-web.js +0 -0
  521. /package/{cjs → dist/cjs}/adaptor.js +0 -0
  522. /package/{cjs → dist/cjs}/ao-loader.js +0 -0
  523. /package/{cjs → dist/cjs}/ao.js +0 -0
  524. /package/{cjs → dist/cjs}/aoconnect-base.js +0 -0
  525. /package/{cjs → dist/cjs}/aoconnect-cf.js +0 -0
  526. /package/{cjs → dist/cjs}/aoconnect-web.js +0 -0
  527. /package/{cjs → dist/cjs}/aoconnect.js +0 -0
  528. /package/{cjs → dist/cjs}/aos_wamr.js +0 -0
  529. /package/{cjs → dist/cjs}/ar-remote.js +0 -0
  530. /package/{cjs → dist/cjs}/ar.js +0 -0
  531. /package/{cjs → dist/cjs}/armem-base.js +0 -0
  532. /package/{cjs → dist/cjs}/armem-cf.js +0 -0
  533. /package/{cjs → dist/cjs}/armem-web.js +0 -0
  534. /package/{cjs → dist/cjs}/armem.js +0 -0
  535. /package/{cjs → dist/cjs}/bao.js +0 -0
  536. /package/{cjs → dist/cjs}/bar.js +0 -0
  537. /package/{cjs → dist/cjs}/build.js +0 -0
  538. /package/{cjs → dist/cjs}/bundler.js +0 -0
  539. /package/{cjs → dist/cjs}/car.js +0 -0
  540. /package/{cjs → dist/cjs}/cf-env.js +0 -0
  541. /package/{cjs → dist/cjs}/cf.js +0 -0
  542. /package/{cjs → dist/cjs}/cli.js +0 -0
  543. /package/{cjs → dist/cjs}/compress.js +0 -0
  544. /package/{cjs → dist/cjs}/create.js +0 -0
  545. /package/{cjs → dist/cjs}/devs.js +0 -0
  546. /package/{cjs → dist/cjs}/dirname.js +0 -0
  547. /package/{cjs → dist/cjs}/dodb.js +0 -0
  548. /package/{cjs → dist/cjs}/erl_json.js +0 -0
  549. /package/{cjs → dist/cjs}/erl_str.js +0 -0
  550. /package/{cjs → dist/cjs}/gql.js +0 -0
  551. /package/{cjs → dist/cjs}/hb.js +0 -0
  552. /package/{cjs → dist/cjs}/helpers.js +0 -0
  553. /package/{cjs → dist/cjs}/http.js +0 -0
  554. /package/{cjs → dist/cjs}/hub/bundler.js +0 -0
  555. /package/{cjs → dist/cjs}/hub/cu.js +0 -0
  556. /package/{cjs → dist/cjs}/hub/fs.js +0 -0
  557. /package/{cjs → dist/cjs}/hub/index.js +0 -0
  558. /package/{cjs → dist/cjs}/hub/utils.js +0 -0
  559. /package/{cjs → dist/cjs}/hub/ws-proxy.js +0 -0
  560. /package/{cjs → dist/cjs}/hyper-aos.js +0 -0
  561. /package/{cjs → dist/cjs}/hyperbeam-server.js +0 -0
  562. /package/{cjs → dist/cjs}/hyperbeam.js +0 -0
  563. /package/{cjs → dist/cjs}/index.js +0 -0
  564. /package/{cjs → dist/cjs}/keygen.js +0 -0
  565. /package/{cjs → dist/cjs}/lfdb.js +0 -0
  566. /package/{cjs → dist/cjs}/lua/aos.wasm +0 -0
  567. /package/{cjs → dist/cjs}/lua/aos2_0_1.js +0 -0
  568. /package/{cjs → dist/cjs}/lua/aos2_0_1.wasm +0 -0
  569. /package/{cjs → dist/cjs}/lua/aos2_0_3.js +0 -0
  570. /package/{cjs → dist/cjs}/lua/aos2_0_3.wasm +0 -0
  571. /package/{cjs → dist/cjs}/lua/aos2_0_4_32.js +0 -0
  572. /package/{cjs → dist/cjs}/lua/aos2_0_4_32.wasm +0 -0
  573. /package/{cjs → dist/cjs}/lua/aos2_0_6.wasm +0 -0
  574. /package/{cjs → dist/cjs}/lua/apm.lua +0 -0
  575. /package/{cjs → dist/cjs}/lua/llama.wasm +0 -0
  576. /package/{cjs → dist/cjs}/lua/process.wasm +0 -0
  577. /package/{cjs → dist/cjs}/lua/sqlite.js +0 -0
  578. /package/{cjs → dist/cjs}/lua/sqlite.wasm +0 -0
  579. /package/{cjs → dist/cjs}/lua/weavedb_mock.lua +0 -0
  580. /package/{cjs → dist/cjs}/lua/weavedrive.lua +0 -0
  581. /package/{cjs → dist/cjs}/run.js +0 -0
  582. /package/{cjs → dist/cjs}/server.js +0 -0
  583. /package/{cjs → dist/cjs}/storage-multi.js +0 -0
  584. /package/{cjs → dist/cjs}/tao.js +0 -0
  585. /package/{cjs → dist/cjs}/tar.js +0 -0
  586. /package/{cjs → dist/cjs}/test.js +0 -0
  587. /package/{cjs → dist/cjs}/tgql-d1.js +0 -0
  588. /package/{cjs → dist/cjs}/tgql.js +0 -0
  589. /package/{cjs → dist/cjs}/toerl.js +0 -0
  590. /package/{cjs → dist/cjs}/utils.js +0 -0
  591. /package/{cjs → dist/cjs}/wao.js +0 -0
  592. /package/{cjs → dist/cjs}/waosm/waosm.js +0 -0
  593. /package/{cjs → dist/cjs}/waosm-node/waosm.js +0 -0
  594. /package/{cjs → dist/cjs}/waosm-node/waosm_bg.js +0 -0
  595. /package/{cjs → dist/cjs}/waosm-node/waosm_bg.wasm +0 -0
  596. /package/{cjs → dist/cjs}/waosm-node/waosm_bg.wasm.js +0 -0
  597. /package/{cjs → dist/cjs}/waosm-node.js +0 -0
  598. /package/{cjs → dist/cjs}/war.js +0 -0
  599. /package/{cjs → dist/cjs}/weavedrive.js +0 -0
  600. /package/{cjs → dist/cjs}/web.js +0 -0
  601. /package/{cjs → dist/cjs}/workspace/.mcp.json +0 -0
  602. /package/{cjs → dist/cjs}/workspace/CLAUDE.md +0 -0
  603. /package/{cjs → dist/cjs}/workspace/README.md +0 -0
  604. /package/{cjs → dist/cjs}/workspace/dashboard/index.html +0 -0
  605. /package/{cjs → dist/cjs}/workspace/dashboard/package.json +0 -0
  606. /package/{esm → dist/cjs}/workspace/dashboard/public/favicon.ico +0 -0
  607. /package/{cjs → dist/cjs}/workspace/dashboard/public/favicon.png +0 -0
  608. /package/{cjs → dist/cjs}/workspace/dashboard/server.js +0 -0
  609. /package/{cjs → dist/cjs}/workspace/dashboard/src/App.jsx +0 -0
  610. /package/{cjs → dist/cjs}/workspace/dashboard/src/main.jsx +0 -0
  611. /package/{cjs → dist/cjs}/workspace/dashboard/vite.config.js +0 -0
  612. /package/{cjs → dist/cjs}/workspace/package.json +0 -0
  613. /package/{esm → dist/cjs}/workspace/src/counter.lua +0 -0
  614. /package/{esm → dist/esm}/accounts-web.js +0 -0
  615. /package/{esm → dist/esm}/accounts.js +0 -0
  616. /package/{esm → dist/esm}/adaptor-base.js +0 -0
  617. /package/{esm → dist/esm}/adaptor-cf.js +0 -0
  618. /package/{esm → dist/esm}/adaptor-web.js +0 -0
  619. /package/{esm → dist/esm}/adaptor.js +0 -0
  620. /package/{esm → dist/esm}/ao-loader.js +0 -0
  621. /package/{esm → dist/esm}/ao.js +0 -0
  622. /package/{esm → dist/esm}/aoconnect-base.js +0 -0
  623. /package/{esm → dist/esm}/aoconnect-cf.js +0 -0
  624. /package/{esm → dist/esm}/aoconnect-web.js +0 -0
  625. /package/{esm → dist/esm}/aoconnect.js +0 -0
  626. /package/{esm → dist/esm}/aos_wamr.js +0 -0
  627. /package/{esm → dist/esm}/ar-remote.js +0 -0
  628. /package/{esm → dist/esm}/ar.js +0 -0
  629. /package/{esm → dist/esm}/armem-base.js +0 -0
  630. /package/{esm → dist/esm}/armem-cf.js +0 -0
  631. /package/{esm → dist/esm}/armem-web.js +0 -0
  632. /package/{esm → dist/esm}/armem.js +0 -0
  633. /package/{esm → dist/esm}/bao.js +0 -0
  634. /package/{esm → dist/esm}/bar.js +0 -0
  635. /package/{esm → dist/esm}/build.js +0 -0
  636. /package/{esm → dist/esm}/bundler.js +0 -0
  637. /package/{esm → dist/esm}/car.js +0 -0
  638. /package/{esm → dist/esm}/cf-env.js +0 -0
  639. /package/{esm → dist/esm}/cf.js +0 -0
  640. /package/{esm → dist/esm}/cli.js +0 -0
  641. /package/{esm → dist/esm}/compress.js +0 -0
  642. /package/{esm → dist/esm}/create.js +0 -0
  643. /package/{esm → dist/esm}/devs.js +0 -0
  644. /package/{esm → dist/esm}/dirname.js +0 -0
  645. /package/{esm → dist/esm}/dodb.js +0 -0
  646. /package/{esm → dist/esm}/erl_json.js +0 -0
  647. /package/{esm → dist/esm}/erl_str.js +0 -0
  648. /package/{esm → dist/esm}/gql.js +0 -0
  649. /package/{esm → dist/esm}/hb.js +0 -0
  650. /package/{esm → dist/esm}/helpers.js +0 -0
  651. /package/{esm → dist/esm}/http.js +0 -0
  652. /package/{esm → dist/esm}/hub/bundler.js +0 -0
  653. /package/{esm → dist/esm}/hub/cu.js +0 -0
  654. /package/{esm → dist/esm}/hub/fs.js +0 -0
  655. /package/{esm → dist/esm}/hub/index.js +0 -0
  656. /package/{esm → dist/esm}/hub/utils.js +0 -0
  657. /package/{esm → dist/esm}/hub/ws-proxy.js +0 -0
  658. /package/{esm → dist/esm}/hyper-aos.js +0 -0
  659. /package/{esm → dist/esm}/hyperbeam-server.js +0 -0
  660. /package/{esm → dist/esm}/hyperbeam.js +0 -0
  661. /package/{esm → dist/esm}/index.js +0 -0
  662. /package/{esm → dist/esm}/keygen.js +0 -0
  663. /package/{esm → dist/esm}/lfdb.js +0 -0
  664. /package/{esm → dist/esm}/lua/aos.wasm +0 -0
  665. /package/{esm → dist/esm}/lua/aos2_0_1.js +0 -0
  666. /package/{esm → dist/esm}/lua/aos2_0_1.wasm +0 -0
  667. /package/{esm → dist/esm}/lua/aos2_0_3.js +0 -0
  668. /package/{esm → dist/esm}/lua/aos2_0_3.wasm +0 -0
  669. /package/{esm → dist/esm}/lua/aos2_0_4_32.js +0 -0
  670. /package/{esm → dist/esm}/lua/aos2_0_4_32.wasm +0 -0
  671. /package/{esm → dist/esm}/lua/aos2_0_6.wasm +0 -0
  672. /package/{esm → dist/esm}/lua/apm.lua +0 -0
  673. /package/{esm → dist/esm}/lua/llama.wasm +0 -0
  674. /package/{esm → dist/esm}/lua/process.wasm +0 -0
  675. /package/{esm → dist/esm}/lua/sqlite.js +0 -0
  676. /package/{esm → dist/esm}/lua/sqlite.wasm +0 -0
  677. /package/{esm → dist/esm}/lua/weavedb_mock.lua +0 -0
  678. /package/{esm → dist/esm}/lua/weavedrive.lua +0 -0
  679. /package/{esm → dist/esm}/package.json +0 -0
  680. /package/{esm → dist/esm}/run.js +0 -0
  681. /package/{esm → dist/esm}/server.js +0 -0
  682. /package/{esm → dist/esm}/storage-multi.js +0 -0
  683. /package/{esm → dist/esm}/tao.js +0 -0
  684. /package/{esm → dist/esm}/tar.js +0 -0
  685. /package/{esm → dist/esm}/test.js +0 -0
  686. /package/{esm → dist/esm}/tgql-d1.js +0 -0
  687. /package/{esm → dist/esm}/tgql.js +0 -0
  688. /package/{esm → dist/esm}/toerl.js +0 -0
  689. /package/{esm → dist/esm}/utils.js +0 -0
  690. /package/{esm → dist/esm}/wao.js +0 -0
  691. /package/{esm → dist/esm}/waosm/README.md +0 -0
  692. /package/{esm → dist/esm}/waosm/waosm.d.ts +0 -0
  693. /package/{esm → dist/esm}/waosm/waosm.js +0 -0
  694. /package/{esm → dist/esm}/waosm/waosm_bg.wasm +0 -0
  695. /package/{esm → dist/esm}/waosm/waosm_bg.wasm.d.ts +0 -0
  696. /package/{esm → dist/esm}/waosm-node/README.md +0 -0
  697. /package/{esm → dist/esm}/waosm-node/waosm.d.ts +0 -0
  698. /package/{esm → dist/esm}/waosm-node/waosm.js +0 -0
  699. /package/{esm → dist/esm}/waosm-node/waosm_bg.js +0 -0
  700. /package/{esm → dist/esm}/waosm-node/waosm_bg.wasm +0 -0
  701. /package/{esm → dist/esm}/waosm-node/waosm_bg.wasm.d.ts +0 -0
  702. /package/{esm → dist/esm}/waosm-node/waosm_bg.wasm.js +0 -0
  703. /package/{esm → dist/esm}/waosm-node.js +0 -0
  704. /package/{esm → dist/esm}/war.js +0 -0
  705. /package/{esm → dist/esm}/weavedrive.js +0 -0
  706. /package/{esm → dist/esm}/web.js +0 -0
  707. /package/{esm → dist/esm}/workspace/.mcp.json +0 -0
  708. /package/{esm → dist/esm}/workspace/CLAUDE.md +0 -0
  709. /package/{esm → dist/esm}/workspace/README.md +0 -0
  710. /package/{esm → dist/esm}/workspace/dashboard/index.html +0 -0
  711. /package/{esm → dist/esm}/workspace/dashboard/package.json +0 -0
  712. /package/{esm/workspace/dashboard/public/favicon.png → dist/esm/workspace/dashboard/public/favicon.ico} +0 -0
  713. /package/{esm → dist/esm}/workspace/dashboard/server.js +0 -0
  714. /package/{esm → dist/esm}/workspace/dashboard/src/App.jsx +0 -0
  715. /package/{cjs/workspace/frontend → dist/esm/workspace/dashboard}/src/main.jsx +0 -0
  716. /package/{esm → dist/esm}/workspace/dashboard/vite.config.js +0 -0
  717. /package/{esm → dist/esm}/workspace/package.json +0 -0
  718. /package/{postinstall.cjs → dist/postinstall.cjs} +0 -0
  719. /package/{esm → src}/workspace/dashboard/src/main.jsx +0 -0
@@ -0,0 +1,1473 @@
1
+ import { queryTxs, queryBlocks, getResults, getResult, getTxData } from "../api.js"
2
+ import { table } from "../components/table.js"
3
+ import { renderTags } from "../components/tags.js"
4
+ import { navigate, trackBlobUrl } from "../router.js"
5
+ import { short, fromNow, tagsMap, formatDate, contentTypeLabel, typeBadgeClass, typeBadgeHtml } from "../utils.js"
6
+
7
+ export async function entityView(el, { id }) {
8
+ el.innerHTML = '<div class="loading">Loading...</div>'
9
+
10
+ try {
11
+ if (/^\d+$/.test(id)) {
12
+ await renderBlock(el, parseInt(id))
13
+ return
14
+ }
15
+
16
+ let data = await queryTxs({ ids: [id] })
17
+ let edge = data?.edges?.[0]
18
+
19
+ // Retry once — GQL index may not have caught up yet
20
+ if (!edge) {
21
+ await new Promise(r => setTimeout(r, 500))
22
+ data = await queryTxs({ ids: [id] })
23
+ edge = data?.edges?.[0]
24
+ }
25
+
26
+ if (!edge) {
27
+ const blocks = await queryBlocks({ first: 1 })
28
+ const found = blocks?.edges?.find((e) => e.node.id === id)
29
+ if (found) {
30
+ await renderBlockNode(el, found.node)
31
+ return
32
+ }
33
+
34
+ const ownerTxs = await queryTxs({ owners: [id], first: 50 })
35
+ if (ownerTxs?.edges?.length) {
36
+ await renderAddress(el, id, ownerTxs)
37
+ return
38
+ }
39
+
40
+ el.innerHTML = `<div class="loading">Entity not found: ${short(id)}</div>`
41
+ return
42
+ }
43
+
44
+ const node = edge.node
45
+ const tags = tagsMap(node.tags)
46
+ const type = tags["Type"] || "Transaction"
47
+
48
+ if (type === "Process") {
49
+ await renderProcess(el, node, tags)
50
+ } else if (type === "Message") {
51
+ await renderMessage(el, node, tags)
52
+ } else if (type === "Module") {
53
+ await renderModule(el, node, tags)
54
+ } else {
55
+ await renderGeneric(el, node, tags, type)
56
+ }
57
+ } catch (err) {
58
+ el.innerHTML = `<div class="loading">Error: ${err.message}</div>`
59
+ }
60
+ }
61
+
62
+ // ---- helpers ----
63
+
64
+ function detailHeader(type, id, name) {
65
+ const div = document.createElement("div")
66
+ div.className = "detail-header"
67
+ let html = `<span class="${typeBadgeClass(type)}">${type}</span>`
68
+ if (name) html += `<div class="entity-name">${name}</div>`
69
+ html += `<div class="entity-id"><h2>${id}</h2><button class="copy-btn" title="Copy to clipboard"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button></div>`
70
+ div.innerHTML = html
71
+ const copyBtn = div.querySelector(".copy-btn")
72
+ copyBtn.addEventListener("click", async () => {
73
+ try {
74
+ await navigator.clipboard.writeText(id)
75
+ copyBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>'
76
+ setTimeout(() => {
77
+ copyBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>'
78
+ }, 1500)
79
+ } catch {}
80
+ })
81
+ return div
82
+ }
83
+
84
+ function detailFields(fields) {
85
+ const div = document.createElement("div")
86
+ div.className = "detail-fields"
87
+ for (const [key, value] of fields) {
88
+ const field = document.createElement("div")
89
+ field.className = "field"
90
+ field.innerHTML = `<div class="field-key">${key}</div><div class="field-value">${value ?? "\u2014"}</div>`
91
+ div.appendChild(field)
92
+ }
93
+ return div
94
+ }
95
+
96
+ function link(text, target) {
97
+ return `<span class="link mono" onclick="location.hash='#/entity/${target}'">${text}</span>`
98
+ }
99
+
100
+ function msgTable(edges) {
101
+ const rows = edges.map((e) => {
102
+ const mt = tagsMap(e.node.tags)
103
+ return {
104
+ action: mt["Action"] || "\u2014",
105
+ id: e.node.id,
106
+ from: e.node.owner?.address || "\u2014",
107
+ to: e.node.recipient || "\u2014",
108
+ block: e.node.block?.height ?? "\u2014",
109
+ time: fromNow(e.node.block?.timestamp),
110
+ _id: e.node.id,
111
+ _from: e.node.owner?.address || "",
112
+ _to: e.node.recipient || "",
113
+ }
114
+ })
115
+ return table(
116
+ [
117
+ { key: "action", label: "Action", render: (v) => `<span class="type-badge-message">${v}</span>` },
118
+ { key: "id", label: "ID", mono: true, render: (v) => `<span class="link">${short(v)}</span>` },
119
+ { key: "from", label: "From", mono: true, render: (v, r) => r._from ? `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._from}'">${short(v)}</span>` : v },
120
+ { key: "to", label: "To", mono: true, render: (v, r) => r._to ? `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._to}'">${short(v)}</span>` : v },
121
+ { key: "block", label: "Block" },
122
+ { key: "time", label: "Time" },
123
+ ],
124
+ rows,
125
+ (row) => navigate("#/entity/" + row._id),
126
+ )
127
+ }
128
+
129
+ function section(title, content) {
130
+ const div = document.createElement("div")
131
+ div.className = "section"
132
+ div.innerHTML = `<div class="section-title">${title}</div>`
133
+ if (typeof content === "string") {
134
+ div.innerHTML += content
135
+ } else if (content) {
136
+ div.appendChild(content)
137
+ }
138
+ return div
139
+ }
140
+
141
+ function twoCol(header, fields, tagsEl) {
142
+ const grid = document.createElement("div")
143
+ grid.className = "detail-grid"
144
+ const left = document.createElement("div")
145
+ left.appendChild(header)
146
+ left.appendChild(fields)
147
+ grid.appendChild(left)
148
+ grid.appendChild(tagsEl)
149
+ return grid
150
+ }
151
+
152
+ // ---- tabs ----
153
+
154
+ function createTabs(tabs) {
155
+ const wrap = document.createElement("div")
156
+ wrap.className = "entity-tabs"
157
+
158
+ const tabBar = document.createElement("div")
159
+ tabBar.className = "tab-bar"
160
+
161
+ const tabContent = document.createElement("div")
162
+ tabContent.className = "tab-content"
163
+
164
+ tabs.forEach((t, i) => {
165
+ const btn = document.createElement("button")
166
+ btn.className = "tab-btn" + (i === 0 ? " active" : "")
167
+ btn.textContent = t.label + (t.count != null ? ` (${t.count})` : "")
168
+ btn.addEventListener("click", () => {
169
+ tabBar.querySelectorAll(".tab-btn").forEach((b) => b.classList.remove("active"))
170
+ btn.classList.add("active")
171
+ tabContent.innerHTML = ""
172
+ if (typeof t.render === "function") {
173
+ const result = t.render()
174
+ if (result instanceof Node) tabContent.appendChild(result)
175
+ }
176
+ })
177
+ tabBar.appendChild(btn)
178
+ })
179
+
180
+ wrap.appendChild(tabBar)
181
+ wrap.appendChild(tabContent)
182
+
183
+ // render first tab
184
+ if (tabs.length && typeof tabs[0].render === "function") {
185
+ const result = tabs[0].render()
186
+ if (result instanceof Node) tabContent.appendChild(result)
187
+ }
188
+
189
+ return wrap
190
+ }
191
+
192
+ // ---- Message tree ----
193
+
194
+ function renderMessageTree(parentNode, parentTags, childMsgs, computeResult, pushedForId, parentAction) {
195
+ const tree = document.createElement("div")
196
+ tree.className = "msg-tree"
197
+
198
+ const target = parentNode.recipient || parentTags["Target"] || ""
199
+ const arrowSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14m-7-7 7 7-7 7"/></svg>'
200
+
201
+ // When parent exists, all nodes (current + children) go in one continuous tree-children
202
+ let childWrap
203
+
204
+ if (pushedForId) {
205
+ // Level 1: parent node
206
+ const ancestor = document.createElement("div")
207
+ ancestor.className = "tree-node tree-root"
208
+ ancestor.innerHTML = `<div class="tree-node-header">
209
+ <span class="tree-node-dot root"></span>
210
+ ${parentAction ? `<span class="type-badge-message">${parentAction}</span>` : ""}
211
+ <span class="mono link tree-node-id" onclick="location.hash='#/entity/${pushedForId}'">${pushedForId}</span>
212
+ </div>`
213
+ tree.appendChild(ancestor)
214
+
215
+ // Level 2: current message nested under parent
216
+ const level2 = document.createElement("div")
217
+ level2.className = "tree-children"
218
+ let selfHtml = `<div class="tree-child"><div class="tree-connector"></div><div class="tree-node-header">
219
+ <span class="tree-node-dot child" style="background:#4ade80;box-shadow:0 0 0 3px #1a3a2a"></span>
220
+ <span class="type-badge-message">${parentTags["Action"] || "Message"}</span>
221
+ <span class="mono tree-node-id">${parentNode.id}</span>`
222
+ if (target) {
223
+ selfHtml += `<span class="tree-node-arrow">${arrowSvg}</span>
224
+ <span class="mono link" onclick="location.hash='#/entity/${target}'">${target}</span>`
225
+ }
226
+ selfHtml += `</div></div>`
227
+ level2.innerHTML = selfHtml
228
+ tree.appendChild(level2)
229
+
230
+ // Level 3: children of the current message, nested one level deeper
231
+ childWrap = document.createElement("div")
232
+ childWrap.className = "tree-children"
233
+ childWrap.style.marginLeft = "24px"
234
+ level2.appendChild(childWrap)
235
+ } else {
236
+ // No parent — this message is the root (self = green)
237
+ const rootItem = document.createElement("div")
238
+ rootItem.className = "tree-node tree-root"
239
+ let rootHtml = `<div class="tree-node-header">
240
+ <span class="tree-node-dot root" style="background:#4ade80;box-shadow:0 0 0 3px #1a3a2a"></span>
241
+ <span class="type-badge-message">${parentTags["Action"] || "Message"}</span>
242
+ <span class="mono tree-node-id">${parentNode.id}</span>`
243
+ if (target) {
244
+ rootHtml += `<span class="tree-node-arrow">${arrowSvg}</span>
245
+ <span class="mono link" onclick="location.hash='#/entity/${target}'">${target}</span>`
246
+ }
247
+ rootHtml += `</div>`
248
+ rootItem.innerHTML = rootHtml
249
+ tree.appendChild(rootItem)
250
+
251
+ // Children container
252
+ childWrap = document.createElement("div")
253
+ childWrap.className = "tree-children"
254
+ }
255
+
256
+ // Show compute result replies first, then recorded GraphQL children (deduplicated).
257
+ const gqlChildren = childMsgs || []
258
+ const resultMsgs = computeResult?.Messages || []
259
+
260
+ // Track reply targets+actions to dedup GraphQL children
261
+ const replyKeys = new Set()
262
+
263
+ // 1. Compute result Messages — always show (replies/unrecorded)
264
+ for (const m of resultMsgs) {
265
+ const mt = tagsMap(m.Tags || m.tags)
266
+ const t = m.Target || m.target || ""
267
+ const action = mt["Action"] || "Message"
268
+ replyKeys.add(`${action}:${t}`)
269
+ const child = document.createElement("div")
270
+ child.className = "tree-node tree-child"
271
+ child.innerHTML = `
272
+ <div class="tree-connector"></div>
273
+ <div class="tree-node-header">
274
+ <span class="tree-node-dot child"></span>
275
+ <span class="type-badge-message">${action}</span>
276
+ <span class="tree-note">(reply)</span>
277
+ ${t ? `<span class="tree-node-arrow">${arrowSvg}</span>
278
+ <span class="mono link" onclick="event.stopPropagation();location.hash='#/entity/${t}'">${t}</span>` : ""}
279
+ </div>
280
+ `
281
+ childWrap.appendChild(child)
282
+ }
283
+
284
+ // 2. GraphQL children (Pushed-For) — skip if already shown as reply
285
+ for (const edge of gqlChildren) {
286
+ const mt = tagsMap(edge.node.tags)
287
+ const action = mt["Action"] || "Message"
288
+ const recipient = edge.node.recipient || ""
289
+ if (replyKeys.has(`${action}:${recipient}`)) continue
290
+ const child = document.createElement("div")
291
+ child.className = "tree-node tree-child"
292
+ child.innerHTML = `
293
+ <div class="tree-connector"></div>
294
+ <div class="tree-node-header">
295
+ <span class="tree-node-dot child"></span>
296
+ <span class="type-badge-message">${action}</span>
297
+ <span class="mono link tree-node-id" onclick="event.stopPropagation();location.hash='#/entity/${edge.node.id}'">${edge.node.id}</span>
298
+ ${recipient ? `<span class="tree-node-arrow">${arrowSvg}</span>
299
+ <span class="mono link" onclick="event.stopPropagation();location.hash='#/entity/${recipient}'">${recipient}</span>` : ""}
300
+ </div>
301
+ `
302
+ childWrap.appendChild(child)
303
+ }
304
+
305
+ // Spawns
306
+ for (const s of computeResult?.Spawns || []) {
307
+ const st = tagsMap(s.Tags || s.tags)
308
+ const child = document.createElement("div")
309
+ child.className = "tree-node tree-child"
310
+ child.innerHTML = `
311
+ <div class="tree-connector"></div>
312
+ <div class="tree-node-header">
313
+ <span class="tree-node-dot spawn"></span>
314
+ <span class="type-badge-process">Spawn</span>
315
+ <span class="tree-node-name">${st["Name"] || "Process"}</span>
316
+ </div>
317
+ `
318
+ childWrap.appendChild(child)
319
+ }
320
+
321
+ if (!childWrap.children.length) {
322
+ const leaf = document.createElement("div")
323
+ leaf.className = "tree-leaf"
324
+ leaf.textContent = "No child messages"
325
+ childWrap.appendChild(leaf)
326
+ }
327
+
328
+ // For no-parent case, childWrap needs to be appended to the tree
329
+ // For parent case, childWrap is already nested inside currentNode
330
+ if (!pushedForId) {
331
+ tree.appendChild(childWrap)
332
+ }
333
+ return tree
334
+ }
335
+
336
+ // ---- Process ----
337
+
338
+ async function renderProcess(el, node, tags) {
339
+ el.innerHTML = ""
340
+ const name = tags["Name"] || null
341
+ const header = detailHeader("Process", node.id, name)
342
+ const fields = detailFields([
343
+ ["Owner", link(node.owner?.address, node.owner?.address)],
344
+ ["Module", tags["Module"] ? link(tags["Module"], tags["Module"]) : "\u2014"],
345
+ ["Name", tags["Name"] || "\u2014"],
346
+ ["Scheduler", tags["Scheduler"] || "\u2014"],
347
+ ["Block", node.block?.height ?? "\u2014"],
348
+ ["Timestamp", formatDate(node.block?.timestamp)],
349
+ ])
350
+ el.appendChild(twoCol(header, fields, section("Tags", renderTags(node.tags))))
351
+
352
+ // Source (Eval Lua script)
353
+ if (parseInt(node.data?.size || "0") > 0) {
354
+ const srcSection = document.createElement("div")
355
+ srcSection.className = "section"
356
+ srcSection.innerHTML = '<div class="section-title">Source</div>'
357
+ el.appendChild(srcSection)
358
+ renderDataViewer(srcSection, node.id, null, node)
359
+ }
360
+
361
+ // Fetch incoming and outgoing messages for this process
362
+ const [incoming, outgoing] = await Promise.all([
363
+ queryTxs({ first: 50, recipients: [node.id], tags: [{ name: "Type", values: ["Message"] }], sort: "HEIGHT_DESC" }).catch(() => null),
364
+ queryTxs({ first: 50, tags: [{ name: "Type", values: ["Message"] }, { name: "From-Process", values: [node.id] }], sort: "HEIGHT_DESC" }).catch(() => null),
365
+ ])
366
+
367
+ const inEdges = incoming?.edges || []
368
+ const outEdges = outgoing?.edges || []
369
+
370
+ // All messages combined, deduped, sorted descending by block height
371
+ const allIds = new Set()
372
+ const allEdges = []
373
+ for (const e of [...inEdges, ...outEdges]) {
374
+ if (!allIds.has(e.node.id)) {
375
+ allIds.add(e.node.id)
376
+ allEdges.push(e)
377
+ }
378
+ }
379
+ allEdges.sort((a, b) => (b.node.block?.height ?? 0) - (a.node.block?.height ?? 0))
380
+
381
+ // Filter eval messages from incoming
382
+ const evalEdges = inEdges.filter((e) => {
383
+ const mt = tagsMap(e.node.tags)
384
+ return mt["Action"] === "Eval"
385
+ })
386
+
387
+ if (allEdges.length || evalEdges.length) {
388
+ const tabs = [
389
+ { label: "All Messages", count: allEdges.length, render: () => msgTable(allEdges) },
390
+ { label: "Incoming", count: inEdges.length, render: () => msgTable(inEdges) },
391
+ { label: "Outgoing", count: outEdges.length, render: () => msgTable(outEdges) },
392
+ ]
393
+ if (evalEdges.length) {
394
+ tabs.push({
395
+ label: "Eval",
396
+ count: evalEdges.length,
397
+ render: () => renderEvalList(evalEdges),
398
+ })
399
+ }
400
+ el.appendChild(createTabs(tabs))
401
+ }
402
+ }
403
+
404
+ function renderEvalList(edges) {
405
+ const wrap = document.createElement("div")
406
+ for (const e of edges) {
407
+ const item = document.createElement("div")
408
+ item.className = "eval-item"
409
+ const header = document.createElement("div")
410
+ header.className = "eval-header"
411
+ header.innerHTML = `
412
+ <span class="mono link" onclick="location.hash='#/entity/${e.node.id}'">${short(e.node.id)}</span>
413
+ <span class="eval-from">from ${short(e.node.owner?.address || "")}</span>
414
+ <span class="eval-time">${fromNow(e.node.block?.timestamp)}</span>
415
+ `
416
+ item.appendChild(header)
417
+
418
+ const codeWrap = document.createElement("div")
419
+ codeWrap.innerHTML = '<pre class="code-block-body" style="margin-top:8px"><code>Loading...</code></pre>'
420
+ item.appendChild(codeWrap)
421
+
422
+ // Load the Lua source async with highlighting + copy btn on header
423
+ getTxData(e.node.id).then((data) => {
424
+ const src = data?.text || "-- (empty)"
425
+ codeWrap.innerHTML = ""
426
+ codeWrap.appendChild(buildCodeBody(src))
427
+ header.appendChild(makeCopyBtn(src))
428
+ }).catch(() => {
429
+ codeWrap.innerHTML = '<pre class="code-block-body" style="margin-top:8px"><code>-- (failed to load)</code></pre>'
430
+ })
431
+
432
+ wrap.appendChild(item)
433
+ }
434
+ return wrap
435
+ }
436
+
437
+ // ---- Compute result with caching ----
438
+
439
+ function cacheKey(mid) { return `cu:${mid}` }
440
+
441
+ function getCachedResult(mid) {
442
+ try {
443
+ const raw = sessionStorage.getItem(cacheKey(mid))
444
+ return raw ? JSON.parse(raw) : null
445
+ } catch { return null }
446
+ }
447
+
448
+ function setCachedResult(mid, result) {
449
+ try { sessionStorage.setItem(cacheKey(mid), JSON.stringify(result)) } catch {}
450
+ }
451
+
452
+ const ANSI_COLORS = {
453
+ "30": "#666", "31": "#e06c75", "32": "#98c379", "33": "#e5c07b",
454
+ "34": "#61afef", "35": "#c678dd", "36": "#56b6c2", "37": "#abb2bf",
455
+ "90": "#5c6370", "91": "#e06c75", "92": "#98c379", "93": "#e5c07b",
456
+ "94": "#61afef", "95": "#c678dd", "96": "#56b6c2", "97": "#ffffff",
457
+ }
458
+
459
+ function ansiToHtml(text) {
460
+ const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
461
+ let html = ""
462
+ let last = 0
463
+ // eslint-disable-next-line no-control-regex
464
+ const re = /\x1b\[([0-9;]*)m/g
465
+ let m
466
+ while ((m = re.exec(text)) !== null) {
467
+ html += esc(text.slice(last, m.index))
468
+ last = m.index + m[0].length
469
+ const codes = m[1]
470
+ if (!codes || codes === "0") {
471
+ html += "</span>"
472
+ } else {
473
+ for (const c of codes.split(";")) {
474
+ if (ANSI_COLORS[c]) html += `<span style="color:${ANSI_COLORS[c]}">`
475
+ else if (c === "1") html += '<span style="font-weight:bold">'
476
+ else if (c === "2") html += '<span style="opacity:0.7">'
477
+ else if (c === "3") html += '<span style="font-style:italic">'
478
+ else if (c === "4") html += '<span style="text-decoration:underline">'
479
+ }
480
+ }
481
+ }
482
+ html += esc(text.slice(last))
483
+ return html
484
+ }
485
+
486
+ function renderAnsiText(text) {
487
+ const div = document.createElement("div")
488
+ // Check if text contains ANSI escape codes
489
+ // eslint-disable-next-line no-control-regex
490
+ if (/\x1b\[/.test(text)) {
491
+ div.innerHTML = ansiToHtml(text)
492
+ } else {
493
+ div.textContent = text
494
+ }
495
+ return div
496
+ }
497
+
498
+ function renderComputeOutput(container, result) {
499
+ if (!result || (!result.Output?.data && !result.Messages?.length && !result.Spawns?.length)) {
500
+ const empty = document.createElement("div")
501
+ empty.className = "loading"
502
+ empty.style.padding = "16px"
503
+ empty.textContent = "No compute result available"
504
+ container.appendChild(empty)
505
+ return
506
+ }
507
+
508
+ if (result.Output?.data) {
509
+ const label = document.createElement("div")
510
+ label.className = "result-label"
511
+ label.textContent = "Output"
512
+ container.appendChild(label)
513
+ const out = document.createElement("div")
514
+ out.className = "result-output"
515
+ const raw = typeof result.Output.data === "string"
516
+ ? result.Output.data
517
+ : JSON.stringify(result.Output.data, null, 2)
518
+ const rendered = renderAnsiText(raw)
519
+ out.appendChild(rendered)
520
+ container.appendChild(out)
521
+ }
522
+
523
+ if (result.Messages?.length) {
524
+ const label = document.createElement("div")
525
+ label.className = "section-title"
526
+ label.textContent = `Spawned Messages (${result.Messages.length})`
527
+ label.style.marginTop = "16px"
528
+ container.appendChild(label)
529
+ const rows = result.Messages.map((m, i) => {
530
+ const mt = tagsMap(m.Tags || m.tags)
531
+ return {
532
+ idx: i + 1,
533
+ action: mt["Action"] || "\u2014",
534
+ target: m.Target || m.target || "\u2014",
535
+ _target: m.Target || m.target || "",
536
+ }
537
+ })
538
+ container.appendChild(table(
539
+ [
540
+ { key: "idx", label: "#" },
541
+ { key: "action", label: "Action" },
542
+ { key: "target", label: "Target", mono: true, render: (v, r) => r._target ? `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._target}'">${short(v)}</span>` : v },
543
+ ],
544
+ rows,
545
+ ))
546
+ }
547
+
548
+ if (result.Spawns?.length) {
549
+ const label = document.createElement("div")
550
+ label.className = "section-title"
551
+ label.textContent = `Spawns (${result.Spawns.length})`
552
+ label.style.marginTop = "16px"
553
+ container.appendChild(label)
554
+ }
555
+ }
556
+
557
+ function highlightLua(code) {
558
+ const keywords = new Set(["and","break","do","else","elseif","end","false","for","function","if","in","local","nil","not","or","repeat","return","then","true","until","while"])
559
+ const builtins = new Set(["print","tostring","tonumber","type","pairs","ipairs","require","error","pcall","xpcall","assert","select","unpack","table","string","math","io","os","ao","Handlers","Send","Spawn","msg"])
560
+ // Escape HTML
561
+ const esc = s => s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
562
+ // Tokenize and highlight
563
+ return code.replace(/--\[\[[\s\S]*?\]\]|--[^\n]*|"[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*'|\[\[[\s\S]*?\]\]|\b\d+\.?\d*\b|[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*|[^\s]/g, token => {
564
+ if (token.startsWith("--")) return `<span class="lua-comment">${esc(token)}</span>`
565
+ if (token.startsWith('"') || token.startsWith("'") || token.startsWith("[[")) return `<span class="lua-string">${esc(token)}</span>`
566
+ if (/^\d/.test(token)) return `<span class="lua-number">${esc(token)}</span>`
567
+ if (/^[A-Za-z_]/.test(token)) {
568
+ // Handle dotted names like ao.send, Handlers.add
569
+ const parts = token.split(".")
570
+ return parts.map(p => {
571
+ if (keywords.has(p)) return `<span class="lua-keyword">${esc(p)}</span>`
572
+ if (builtins.has(p)) return `<span class="lua-builtin">${esc(p)}</span>`
573
+ return esc(p)
574
+ }).join(`<span class="lua-punct">.</span>`)
575
+ }
576
+ return esc(token)
577
+ })
578
+ }
579
+
580
+ const clipSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'
581
+ const checkSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>'
582
+
583
+ function makeCopyBtn(text) {
584
+ const btn = document.createElement("button")
585
+ btn.className = "code-copy-btn"
586
+ btn.title = "Copy to clipboard"
587
+ btn.innerHTML = clipSvg
588
+ btn.onclick = () => {
589
+ navigator.clipboard.writeText(text).then(() => {
590
+ btn.innerHTML = checkSvg
591
+ setTimeout(() => { btn.innerHTML = clipSvg }, 1500)
592
+ })
593
+ }
594
+ return btn
595
+ }
596
+
597
+ function buildCodeBody(src) {
598
+ const wrap = document.createElement("div")
599
+ wrap.className = "code-block-body"
600
+ const lines = src.split("\n")
601
+ const gutter = document.createElement("pre")
602
+ gutter.className = "code-gutter"
603
+ gutter.textContent = lines.map((_, i) => i + 1).join("\n")
604
+ const code = document.createElement("pre")
605
+ code.className = "code-content"
606
+ code.innerHTML = highlightLua(src)
607
+ wrap.appendChild(gutter)
608
+ wrap.appendChild(code)
609
+ return wrap
610
+ }
611
+
612
+ // ---- Simple markdown renderer ----
613
+
614
+ function renderMarkdown(text) {
615
+ const esc = s => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
616
+ const lines = text.split("\n")
617
+ let html = ""
618
+ let inCode = false
619
+ let inList = false
620
+ let listType = null
621
+
622
+ for (let i = 0; i < lines.length; i++) {
623
+ const line = lines[i]
624
+
625
+ // Fenced code blocks
626
+ if (line.trimStart().startsWith("```")) {
627
+ if (inCode) {
628
+ html += "</code></pre>"
629
+ inCode = false
630
+ } else {
631
+ if (inList) { html += listType === "ul" ? "</ul>" : "</ol>"; inList = false }
632
+ html += '<pre class="data-md-code"><code>'
633
+ inCode = true
634
+ }
635
+ continue
636
+ }
637
+ if (inCode) {
638
+ html += esc(line) + "\n"
639
+ continue
640
+ }
641
+
642
+ // Close list if line is not a list item
643
+ if (inList && !/^\s*[-*]\s|^\s*\d+\.\s/.test(line) && line.trim() !== "") {
644
+ html += listType === "ul" ? "</ul>" : "</ol>"
645
+ inList = false
646
+ }
647
+
648
+ // Blank line
649
+ if (line.trim() === "") {
650
+ if (inList) { html += listType === "ul" ? "</ul>" : "</ol>"; inList = false }
651
+ continue
652
+ }
653
+
654
+ // Headings
655
+ const hm = line.match(/^(#{1,6})\s+(.*)/)
656
+ if (hm) {
657
+ const level = hm[1].length
658
+ html += `<h${level}>${inlineFormat(esc(hm[2]))}</h${level}>`
659
+ continue
660
+ }
661
+
662
+ // Unordered list
663
+ if (/^\s*[-*]\s/.test(line)) {
664
+ if (!inList || listType !== "ul") {
665
+ if (inList) html += listType === "ul" ? "</ul>" : "</ol>"
666
+ html += "<ul>"
667
+ inList = true
668
+ listType = "ul"
669
+ }
670
+ html += `<li>${inlineFormat(esc(line.replace(/^\s*[-*]\s/, "")))}</li>`
671
+ continue
672
+ }
673
+
674
+ // Ordered list
675
+ if (/^\s*\d+\.\s/.test(line)) {
676
+ if (!inList || listType !== "ol") {
677
+ if (inList) html += listType === "ul" ? "</ul>" : "</ol>"
678
+ html += "<ol>"
679
+ inList = true
680
+ listType = "ol"
681
+ }
682
+ html += `<li>${inlineFormat(esc(line.replace(/^\s*\d+\.\s/, "")))}</li>`
683
+ continue
684
+ }
685
+
686
+ // Paragraph
687
+ html += `<p>${inlineFormat(esc(line))}</p>`
688
+ }
689
+
690
+ if (inCode) html += "</code></pre>"
691
+ if (inList) html += listType === "ul" ? "</ul>" : "</ol>"
692
+ return html
693
+ }
694
+
695
+ function inlineFormat(text) {
696
+ return text
697
+ .replace(/`([^`]+)`/g, "<code>$1</code>")
698
+ .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
699
+ .replace(/\*([^*]+)\*/g, "<em>$1</em>")
700
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>')
701
+ }
702
+
703
+ // ---- Hex dump renderer ----
704
+
705
+ function renderHexDump(bytes) {
706
+ const view = new Uint8Array(bytes)
707
+ const limit = Math.min(view.length, 256)
708
+ let html = ""
709
+ for (let i = 0; i < limit; i += 16) {
710
+ const addr = i.toString(16).padStart(8, "0")
711
+ let hex = ""
712
+ let ascii = ""
713
+ for (let j = 0; j < 16; j++) {
714
+ if (i + j < limit) {
715
+ const b = view[i + j]
716
+ hex += b.toString(16).padStart(2, "0") + " "
717
+ ascii += b >= 0x20 && b <= 0x7e ? String.fromCharCode(b) : "."
718
+ } else {
719
+ hex += " "
720
+ ascii += " "
721
+ }
722
+ if (j === 7) hex += " "
723
+ }
724
+ html += `<div class="hex-row"><span class="hex-addr">${addr}</span><span class="hex-bytes">${hex}</span><span class="hex-ascii">${ascii}</span></div>`
725
+ }
726
+ if (view.length > limit) {
727
+ html += `<div class="hex-row hex-truncated">... ${view.length - limit} more bytes</div>`
728
+ }
729
+ return html
730
+ }
731
+
732
+ // ---- JSON collapsible viewer ----
733
+
734
+ const JSON_MAX_DEPTH = 20
735
+ const JSON_MAX_KEYS = 200
736
+
737
+ function renderJsonTree(obj, depth = 0) {
738
+ if (depth > JSON_MAX_DEPTH) return '<span class="json-null">...</span>'
739
+ if (obj === null) return '<span class="json-null">null</span>'
740
+ if (typeof obj === "boolean") return `<span class="json-bool">${obj}</span>`
741
+ if (typeof obj === "number") return `<span class="json-number">${obj}</span>`
742
+ if (typeof obj === "string") {
743
+ const display = obj.length > 10000 ? obj.slice(0, 10000) + "..." : obj
744
+ const esc = display.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
745
+ return `<span class="json-string">"${esc}"</span>`
746
+ }
747
+ if (Array.isArray(obj)) {
748
+ if (obj.length === 0) return "[]"
749
+ const limited = obj.slice(0, JSON_MAX_KEYS)
750
+ const items = limited.map(v => `<div class="json-indent">${renderJsonTree(v, depth + 1)},</div>`).join("")
751
+ const truncated = obj.length > JSON_MAX_KEYS ? `<div class="json-indent"><span class="json-null">... ${obj.length - JSON_MAX_KEYS} more items</span></div>` : ""
752
+ return `[${items}${truncated}]`
753
+ }
754
+ if (typeof obj === "object") {
755
+ const keys = Object.keys(obj)
756
+ if (keys.length === 0) return "{}"
757
+ const limited = keys.slice(0, JSON_MAX_KEYS)
758
+ const items = limited.map(k => {
759
+ const esc = k.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
760
+ return `<div class="json-indent"><span class="json-key">"${esc}"</span>: ${renderJsonTree(obj[k], depth + 1)},</div>`
761
+ }).join("")
762
+ const truncated = keys.length > JSON_MAX_KEYS ? `<div class="json-indent"><span class="json-null">... ${keys.length - JSON_MAX_KEYS} more keys</span></div>` : ""
763
+ return `{${items}${truncated}}`
764
+ }
765
+ return String(obj)
766
+ }
767
+
768
+ // ---- Data viewer header ----
769
+
770
+ function dataViewerHeader(contentType, size, blobUrl, filename, text) {
771
+ const header = document.createElement("div")
772
+ header.className = "data-viewer-header"
773
+ const badge = document.createElement("span")
774
+ badge.className = "data-ct-badge"
775
+ badge.textContent = contentType
776
+ header.appendChild(badge)
777
+
778
+ const sizeEl = document.createElement("span")
779
+ sizeEl.className = "data-size"
780
+ sizeEl.textContent = size < 1024 ? `${size} B` : size < 1048576 ? `${(size / 1024).toFixed(1)} KB` : `${(size / 1048576).toFixed(1)} MB`
781
+ header.appendChild(sizeEl)
782
+
783
+ const spacer = document.createElement("span")
784
+ spacer.style.flex = "1"
785
+ header.appendChild(spacer)
786
+
787
+ if (text !== null) {
788
+ header.appendChild(makeCopyBtn(text))
789
+ }
790
+
791
+ const dlBtn = document.createElement("a")
792
+ dlBtn.className = "data-download-btn"
793
+ dlBtn.href = blobUrl
794
+ dlBtn.download = filename
795
+ dlBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> Download'
796
+ header.appendChild(dlBtn)
797
+
798
+ return header
799
+ }
800
+
801
+ // ---- Rich data viewer (replaces autoLoadData) ----
802
+
803
+ async function renderDataViewer(container, txId, action, node) {
804
+ const loading = document.createElement("div")
805
+ loading.className = "loading"
806
+ loading.style.padding = "12px"
807
+ loading.textContent = "Loading data..."
808
+ container.appendChild(loading)
809
+ try {
810
+ const data = await getTxData(txId)
811
+ loading.remove()
812
+ if (!data) {
813
+ const empty = document.createElement("div")
814
+ empty.className = "data-preview"
815
+ empty.textContent = "No data"
816
+ container.appendChild(empty)
817
+ return
818
+ }
819
+
820
+ const { contentType, blob, text, size } = data
821
+
822
+ // Handle files too large for in-browser rendering
823
+ if (data.tooLarge) {
824
+ const info = document.createElement("div")
825
+ info.className = "data-viewer"
826
+ const sizeStr = size < 1048576 ? (size / 1024).toFixed(1) + " KB" : (size / 1048576).toFixed(1) + " MB"
827
+ info.innerHTML = `<div class="data-viewer-header"><span class="data-ct-badge">${contentType}</span><span class="data-size">${sizeStr}</span><span style="flex:1"></span><a class="data-download-btn" href="/ar/${txId}" download="${txId}"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> Download</a></div><div class="data-preview">File too large for in-browser preview (${sizeStr}). Use the download button.</div>`
828
+ container.appendChild(info)
829
+ return
830
+ }
831
+
832
+ const blobUrl = URL.createObjectURL(blob)
833
+ trackBlobUrl(blobUrl)
834
+ const viewer = document.createElement("div")
835
+ viewer.className = "data-viewer"
836
+
837
+ // Header with badge, size, buttons
838
+ viewer.appendChild(dataViewerHeader(contentType, size, blobUrl, txId, text))
839
+
840
+ // Dispatch on content type
841
+ if (action === "Eval" || contentType === "text/x-lua") {
842
+ // Lua code with syntax highlighting
843
+ viewer.appendChild(buildCodeBody(text || "-- (empty)"))
844
+ } else if (contentType.startsWith("image/svg+xml") && text) {
845
+ // SVG: render + toggle to code (sanitize scripts)
846
+ const btnGroup = document.createElement("div")
847
+ btnGroup.className = "data-view-tabs"
848
+ const btnPreview = document.createElement("button")
849
+ btnPreview.className = "data-view-tab active"
850
+ btnPreview.textContent = "Preview"
851
+ const btnSource = document.createElement("button")
852
+ btnSource.className = "data-view-tab"
853
+ btnSource.textContent = "Source"
854
+ btnGroup.appendChild(btnPreview)
855
+ btnGroup.appendChild(btnSource)
856
+ viewer.firstChild.insertBefore(btnGroup, viewer.firstChild.querySelector(".data-size").nextSibling)
857
+
858
+ const svgContainer = document.createElement("div")
859
+ svgContainer.className = "data-svg-wrap"
860
+ const svgRender = document.createElement("div")
861
+ svgRender.className = "data-svg-render"
862
+ const sanitizedSvg = text.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/\bon\w+\s*=/gi, "data-removed=")
863
+ svgRender.innerHTML = sanitizedSvg
864
+ const codeView = document.createElement("div")
865
+ codeView.className = "data-svg-code"
866
+ codeView.style.display = "none"
867
+ codeView.appendChild(buildGenericCodeBody(text))
868
+ btnPreview.addEventListener("click", () => {
869
+ svgRender.style.display = ""
870
+ codeView.style.display = "none"
871
+ btnPreview.classList.add("active")
872
+ btnSource.classList.remove("active")
873
+ })
874
+ btnSource.addEventListener("click", () => {
875
+ svgRender.style.display = "none"
876
+ codeView.style.display = ""
877
+ btnSource.classList.add("active")
878
+ btnPreview.classList.remove("active")
879
+ })
880
+ svgContainer.appendChild(svgRender)
881
+ svgContainer.appendChild(codeView)
882
+ viewer.appendChild(svgContainer)
883
+ } else if (contentType.startsWith("image/")) {
884
+ const box = document.createElement("div")
885
+ box.className = "data-media-box"
886
+ const img = document.createElement("img")
887
+ img.className = "data-img"
888
+ img.src = blobUrl
889
+ img.alt = "Transaction data"
890
+ box.appendChild(img)
891
+ viewer.appendChild(box)
892
+ } else if (contentType.startsWith("video/")) {
893
+ const box = document.createElement("div")
894
+ box.className = "data-video-box"
895
+ const video = document.createElement("video")
896
+ video.className = "data-video"
897
+ video.src = blobUrl
898
+ video.controls = true
899
+ box.appendChild(video)
900
+ viewer.appendChild(box)
901
+ } else if (contentType.startsWith("audio/")) {
902
+ const audio = document.createElement("audio")
903
+ audio.src = blobUrl
904
+ audio.controls = true
905
+ audio.style.width = "100%"
906
+ viewer.appendChild(audio)
907
+ } else if (contentType === "text/markdown" && text) {
908
+ // Add view mode buttons to header
909
+ const btnGroup = document.createElement("div")
910
+ btnGroup.className = "data-view-tabs"
911
+ const btnPreview = document.createElement("button")
912
+ btnPreview.className = "data-view-tab active"
913
+ btnPreview.textContent = "Preview"
914
+ const btnSource = document.createElement("button")
915
+ btnSource.className = "data-view-tab"
916
+ btnSource.textContent = "Source"
917
+ btnGroup.appendChild(btnPreview)
918
+ btnGroup.appendChild(btnSource)
919
+ viewer.firstChild.insertBefore(btnGroup, viewer.firstChild.querySelector(".data-size").nextSibling)
920
+
921
+ const mdWrap = document.createElement("div")
922
+ mdWrap.className = "data-md-wrap"
923
+ const mdPreview = document.createElement("div")
924
+ mdPreview.className = "data-markdown"
925
+ mdPreview.innerHTML = renderMarkdown(text)
926
+ const mdCode = document.createElement("div")
927
+ mdCode.style.display = "none"
928
+ mdCode.appendChild(buildGenericCodeBody(text))
929
+ btnPreview.addEventListener("click", () => {
930
+ mdPreview.style.display = ""
931
+ mdCode.style.display = "none"
932
+ btnPreview.classList.add("active")
933
+ btnSource.classList.remove("active")
934
+ })
935
+ btnSource.addEventListener("click", () => {
936
+ mdPreview.style.display = "none"
937
+ mdCode.style.display = ""
938
+ btnSource.classList.add("active")
939
+ btnPreview.classList.remove("active")
940
+ })
941
+ mdWrap.appendChild(mdPreview)
942
+ mdWrap.appendChild(mdCode)
943
+ viewer.appendChild(mdWrap)
944
+ } else if (contentType === "text/html" && text) {
945
+ const iframe = document.createElement("iframe")
946
+ iframe.className = "data-html-frame"
947
+ iframe.sandbox = "allow-scripts"
948
+ iframe.srcdoc = text
949
+ viewer.appendChild(iframe)
950
+ } else if (contentType.includes("json") && text) {
951
+ try {
952
+ const obj = JSON.parse(text)
953
+ const pre = document.createElement("pre")
954
+ pre.className = "data-json"
955
+ pre.innerHTML = renderJsonTree(obj)
956
+ viewer.appendChild(pre)
957
+ } catch {
958
+ viewer.appendChild(buildGenericCodeBody(text))
959
+ }
960
+ } else if (
961
+ contentType.includes("javascript") || contentType.includes("typescript") ||
962
+ contentType.includes("ecmascript") || contentType.includes("css") ||
963
+ contentType.includes("x-python") || contentType.includes("python") ||
964
+ contentType.includes("x-toml") || contentType.includes("x-yaml") ||
965
+ contentType.includes("x-sh") || contentType.includes("shell") ||
966
+ contentType.includes("x-rust") || contentType.includes("x-go") ||
967
+ contentType.includes("x-c") || contentType.includes("x-java")
968
+ ) {
969
+ viewer.appendChild(buildGenericCodeBody(text || "", contentType))
970
+ } else if (text !== null) {
971
+ // Fallback: plain text with ANSI support
972
+ const pre = document.createElement("div")
973
+ pre.className = "data-preview"
974
+ pre.appendChild(renderAnsiText(text))
975
+ viewer.appendChild(pre)
976
+ } else {
977
+ // Binary fallback — just show info, no hex dump
978
+ const info = document.createElement("div")
979
+ info.className = "data-preview"
980
+ info.textContent = `Binary data (${size < 1024 ? size + " bytes" : size < 1048576 ? (size / 1024).toFixed(1) + " KB" : (size / 1048576).toFixed(1) + " MB"})`
981
+ viewer.appendChild(info)
982
+ }
983
+
984
+ container.appendChild(viewer)
985
+
986
+ // Bundle items: if this tx has Bundle-Format tag, show child items
987
+ if (node) {
988
+ const tags = node.tags || []
989
+ const hasBundleFormat = tags.some(t => t.name === "Bundle-Format")
990
+ if (hasBundleFormat) {
991
+ await renderBundleItems(container, txId)
992
+ }
993
+ }
994
+ } catch {
995
+ loading.remove()
996
+ }
997
+ }
998
+
999
+ // ---- Generic code body with syntax highlighting ----
1000
+
1001
+ function highlightGeneric(code, lang) {
1002
+ const esc = s => s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
1003
+ const kw = {
1004
+ js: ["const","let","var","function","return","if","else","for","while","do","switch","case","break","continue","new","this","class","extends","import","export","from","default","async","await","try","catch","throw","finally","typeof","instanceof","in","of","yield","delete","void","null","undefined","true","false","super","static","get","set"],
1005
+ py: ["def","class","return","if","elif","else","for","while","try","except","finally","with","as","import","from","raise","pass","break","continue","yield","lambda","and","or","not","in","is","True","False","None","global","nonlocal","assert","del","async","await"],
1006
+ css: ["@media","@keyframes","@import","@font-face","@charset","@supports","!important"],
1007
+ sh: ["if","then","else","elif","fi","for","while","do","done","case","esac","in","function","return","local","export","source","alias","echo","exit","read","set","unset","shift","trap","eval","exec","test"],
1008
+ toml: [],
1009
+ yaml: ["true","false","null","yes","no","on","off"],
1010
+ }
1011
+ const keywords = new Set(kw[lang] || kw.js)
1012
+ const lineComment = lang === "py" || lang === "sh" || lang === "toml" || lang === "yaml" ? "#" : "//"
1013
+ const hasBlock = lang === "js" || lang === "css"
1014
+
1015
+ let result = ""
1016
+ let i = 0
1017
+ while (i < code.length) {
1018
+ // Block comments
1019
+ if (hasBlock && code[i] === "/" && code[i+1] === "*") {
1020
+ const end = code.indexOf("*/", i + 2)
1021
+ const slice = end === -1 ? code.slice(i) : code.slice(i, end + 2)
1022
+ result += `<span class="lua-comment">${esc(slice)}</span>`
1023
+ i += slice.length
1024
+ continue
1025
+ }
1026
+ // Line comments
1027
+ if (code.slice(i, i + lineComment.length) === lineComment) {
1028
+ const nl = code.indexOf("\n", i)
1029
+ const slice = nl === -1 ? code.slice(i) : code.slice(i, nl)
1030
+ result += `<span class="lua-comment">${esc(slice)}</span>`
1031
+ i += slice.length
1032
+ continue
1033
+ }
1034
+ // Strings
1035
+ if (code[i] === '"' || code[i] === "'" || code[i] === "`") {
1036
+ const q = code[i]
1037
+ let j = i + 1
1038
+ while (j < code.length && code[j] !== q) {
1039
+ if (code[j] === "\\") j++
1040
+ j++
1041
+ }
1042
+ if (j < code.length) j++
1043
+ result += `<span class="lua-string">${esc(code.slice(i, j))}</span>`
1044
+ i = j
1045
+ continue
1046
+ }
1047
+ // Numbers
1048
+ if (/[0-9]/.test(code[i]) && (i === 0 || /[\s,;:({[=+\-*/%<>!&|^~?]/.test(code[i-1]))) {
1049
+ let j = i
1050
+ if (code[j] === "0" && (code[j+1] === "x" || code[j+1] === "X")) j += 2
1051
+ while (j < code.length && /[0-9a-fA-F._]/.test(code[j])) j++
1052
+ result += `<span class="lua-number">${esc(code.slice(i, j))}</span>`
1053
+ i = j
1054
+ continue
1055
+ }
1056
+ // Identifiers / keywords
1057
+ if (/[A-Za-z_$]/.test(code[i])) {
1058
+ let j = i
1059
+ while (j < code.length && /[A-Za-z0-9_$]/.test(code[j])) j++
1060
+ const word = code.slice(i, j)
1061
+ if (keywords.has(word)) {
1062
+ result += `<span class="lua-keyword">${esc(word)}</span>`
1063
+ } else {
1064
+ result += esc(word)
1065
+ }
1066
+ i = j
1067
+ continue
1068
+ }
1069
+ result += esc(code[i])
1070
+ i++
1071
+ }
1072
+ return result
1073
+ }
1074
+
1075
+ function detectLang(contentType) {
1076
+ if (contentType.includes("javascript") || contentType.includes("ecmascript")) return "js"
1077
+ if (contentType.includes("typescript")) return "js"
1078
+ if (contentType.includes("python")) return "py"
1079
+ if (contentType.includes("css")) return "css"
1080
+ if (contentType.includes("x-sh") || contentType.includes("shell") || contentType.includes("bash")) return "sh"
1081
+ if (contentType.includes("toml")) return "toml"
1082
+ if (contentType.includes("yaml") || contentType.includes("yml")) return "yaml"
1083
+ if (contentType.includes("lua")) return "lua"
1084
+ return "js" // default fallback
1085
+ }
1086
+
1087
+ function buildGenericCodeBody(src, contentType) {
1088
+ const wrap = document.createElement("div")
1089
+ wrap.className = "code-block-body"
1090
+ const lines = src.split("\n")
1091
+ const gutter = document.createElement("pre")
1092
+ gutter.className = "code-gutter"
1093
+ gutter.textContent = lines.map((_, i) => i + 1).join("\n")
1094
+ const code = document.createElement("pre")
1095
+ code.className = "code-content"
1096
+ if (contentType) {
1097
+ const lang = detectLang(contentType)
1098
+ if (lang === "lua") {
1099
+ code.innerHTML = highlightLua(src)
1100
+ } else {
1101
+ code.innerHTML = highlightGeneric(src, lang)
1102
+ }
1103
+ } else {
1104
+ code.textContent = src
1105
+ }
1106
+ wrap.appendChild(gutter)
1107
+ wrap.appendChild(code)
1108
+ return wrap
1109
+ }
1110
+
1111
+ // ---- Bundle items UI ----
1112
+
1113
+ async function renderBundleItems(container, bundleId) {
1114
+ try {
1115
+ const items = await queryTxs({
1116
+ first: 50,
1117
+ bundledIn: [bundleId],
1118
+ }).catch(() => null)
1119
+
1120
+ // Also try bundledIn filter via owner query (items reference parent)
1121
+ let edges = items?.edges || []
1122
+
1123
+ // If that didn't find anything, try a broader approach
1124
+ if (edges.length === 0) return
1125
+
1126
+ const sec = document.createElement("div")
1127
+ sec.className = "section bundle-items"
1128
+ sec.innerHTML = `<div class="section-title">Bundle Items (${edges.length})</div>`
1129
+
1130
+ const rows = edges.map(e => {
1131
+ const mt = tagsMap(e.node.tags)
1132
+ return {
1133
+ type: mt["Type"] || "Data",
1134
+ name: mt["Name"] || "\u2014",
1135
+ id: e.node.id,
1136
+ owner: e.node.owner?.address || "\u2014",
1137
+ size: e.node.data?.size || "\u2014",
1138
+ _id: e.node.id,
1139
+ _owner: e.node.owner?.address || "",
1140
+ }
1141
+ })
1142
+
1143
+ sec.appendChild(table(
1144
+ [
1145
+ { key: "type", label: "Type", render: v => typeBadgeHtml(v) },
1146
+ { key: "name", label: "Name" },
1147
+ { key: "id", label: "ID", mono: true, render: v => `<span class="link">${short(v)}</span>` },
1148
+ { key: "owner", label: "Owner", mono: true, render: (v, r) => r._owner ? `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._owner}'">${short(v)}</span>` : v },
1149
+ { key: "size", label: "Data Size" },
1150
+ ],
1151
+ rows,
1152
+ row => navigate("#/entity/" + row._id),
1153
+ ))
1154
+
1155
+ container.appendChild(sec)
1156
+ } catch {
1157
+ // ok
1158
+ }
1159
+ }
1160
+
1161
+ async function loadComputeResult(container, mid, pid) {
1162
+ // Check cache first
1163
+ const cached = getCachedResult(mid)
1164
+ if (cached) {
1165
+ renderComputeOutput(container, cached)
1166
+ return cached
1167
+ }
1168
+
1169
+ // Auto-load
1170
+ const loading = document.createElement("div")
1171
+ loading.className = "loading"
1172
+ loading.style.padding = "12px"
1173
+ loading.textContent = "Loading compute result..."
1174
+ container.appendChild(loading)
1175
+
1176
+ try {
1177
+ const result = await getResult(mid, pid)
1178
+ loading.remove()
1179
+ setCachedResult(mid, result)
1180
+ renderComputeOutput(container, result)
1181
+ return result
1182
+ } catch (err) {
1183
+ loading.remove()
1184
+ const errDiv = document.createElement("div")
1185
+ errDiv.className = "loading"
1186
+ errDiv.style.padding = "8px"
1187
+ errDiv.textContent = err.message || "Failed to fetch compute result"
1188
+ container.appendChild(errDiv)
1189
+ return null
1190
+ }
1191
+ }
1192
+
1193
+ // ---- Message ----
1194
+
1195
+ async function renderMessage(el, node, tags) {
1196
+ el.innerHTML = ""
1197
+ const action = tags["Action"] || null
1198
+ const header = detailHeader("Message", node.id, action)
1199
+
1200
+ const target = node.recipient || tags["Target"]
1201
+ const pushedFor = tags["Pushed-For"]
1202
+ const fromProcess = tags["From-Process"]
1203
+ const fieldRows = [
1204
+ ["Action", tags["Action"] || "\u2014"],
1205
+ ["From", link(node.owner?.address, node.owner?.address)],
1206
+ ["To (Process)", target ? link(target, target) : "\u2014"],
1207
+ ]
1208
+ if (pushedFor) fieldRows.push(["Parent Message", link(pushedFor, pushedFor)])
1209
+ if (fromProcess) fieldRows.push(["From Process", link(fromProcess, fromProcess)])
1210
+ fieldRows.push(
1211
+ ["Block", node.block?.height ?? "\u2014"],
1212
+ ["Timestamp", formatDate(node.block?.timestamp)],
1213
+ ["Data Size", node.data?.size ? node.data.size + " bytes" : "\u2014"],
1214
+ )
1215
+ const fields = detailFields(fieldRows)
1216
+ el.appendChild(twoCol(header, fields, section("Tags", renderTags(node.tags))))
1217
+
1218
+ // Data — auto-load
1219
+ if (parseInt(node.data?.size || "0") > 0) {
1220
+ const dataSection = document.createElement("div")
1221
+ dataSection.className = "section"
1222
+ dataSection.innerHTML = '<div class="section-title">Data</div>'
1223
+ el.appendChild(dataSection)
1224
+ renderDataViewer(dataSection, node.id, tags["Action"], node)
1225
+ }
1226
+
1227
+ // Compute Result — auto-load with caching
1228
+ let computeResult = null
1229
+ if (target) {
1230
+ const resultSection = document.createElement("div")
1231
+ resultSection.className = "section"
1232
+ resultSection.innerHTML = '<div class="section-title">Compute Result</div>'
1233
+ el.appendChild(resultSection)
1234
+ computeResult = await loadComputeResult(resultSection, node.id, target)
1235
+ }
1236
+
1237
+ // Message Tree — always show, with parent-child relationships
1238
+ // Find child messages + parent action in parallel
1239
+ let childMsgs = []
1240
+ let parentAction = null
1241
+ {
1242
+ const promises = []
1243
+ promises.push(
1244
+ queryTxs({
1245
+ first: 50,
1246
+ tags: [
1247
+ { name: "Type", values: ["Message"] },
1248
+ { name: "Pushed-For", values: [node.id] },
1249
+ ],
1250
+ sort: "HEIGHT_DESC",
1251
+ }).then(r => { childMsgs = r?.edges || [] }).catch(() => {})
1252
+ )
1253
+ if (pushedFor) {
1254
+ promises.push(
1255
+ queryTxs({ ids: [pushedFor] }).then(r => {
1256
+ const pt = tagsMap(r?.edges?.[0]?.node?.tags)
1257
+ parentAction = pt["Action"] || null
1258
+ }).catch(() => {})
1259
+ )
1260
+ }
1261
+ await Promise.all(promises)
1262
+ }
1263
+
1264
+ {
1265
+ const treeSection = document.createElement("div")
1266
+ treeSection.className = "section"
1267
+ treeSection.innerHTML = '<div class="section-title">Message Flow</div>'
1268
+
1269
+ treeSection.appendChild(renderMessageTree(node, tags, childMsgs, computeResult, pushedFor, parentAction))
1270
+ el.appendChild(treeSection)
1271
+ }
1272
+
1273
+ }
1274
+
1275
+ // ---- Module ----
1276
+
1277
+ async function renderModule(el, node, tags) {
1278
+ el.innerHTML = ""
1279
+ const name = tags["Name"] || tags["Module-Name"] || null
1280
+ const header = detailHeader("Module", node.id, name)
1281
+ const fields = detailFields([
1282
+ ["Owner", link(node.owner?.address, node.owner?.address)],
1283
+ ["Name", tags["Name"] || tags["Module-Name"] || "\u2014"],
1284
+ ["Format", tags["Module-Format"] || tags["Content-Type"] || "\u2014"],
1285
+ ["Memory Limit", tags["Memory-Limit"] || "\u2014"],
1286
+ ["Compute Limit", tags["Compute-Limit"] || "\u2014"],
1287
+ ["Input Encoding", tags["Input-Encoding"] || "\u2014"],
1288
+ ["Output Encoding", tags["Output-Encoding"] || "\u2014"],
1289
+ ["Block", node.block?.height ?? "\u2014"],
1290
+ ["Timestamp", formatDate(node.block?.timestamp)],
1291
+ ["Data Size", node.data?.size ? node.data.size + " bytes" : "\u2014"],
1292
+ ])
1293
+ el.appendChild(twoCol(header, fields, section("Tags", renderTags(node.tags))))
1294
+
1295
+ // Processes using this module
1296
+ try {
1297
+ const procs = await queryTxs({
1298
+ first: 50,
1299
+ tags: [
1300
+ { name: "Type", values: ["Process"] },
1301
+ { name: "Module", values: [node.id] },
1302
+ ],
1303
+ sort: "HEIGHT_DESC",
1304
+ })
1305
+ const procEdges = procs?.edges || []
1306
+ const rows = procEdges.map((e) => {
1307
+ const pt = tagsMap(e.node.tags)
1308
+ return {
1309
+ name: pt["Name"] || "\u2014",
1310
+ id: e.node.id,
1311
+ owner: e.node.owner?.address,
1312
+ time: fromNow(e.node.block?.timestamp),
1313
+ _id: e.node.id,
1314
+ _owner: e.node.owner?.address || "",
1315
+ }
1316
+ })
1317
+ if (rows.length) {
1318
+ el.appendChild(section(
1319
+ `Processes (${procEdges.length})`,
1320
+ table(
1321
+ [
1322
+ { key: "name", label: "Name" },
1323
+ { key: "id", label: "ID", mono: true, render: (v) => `<span class="link">${short(v)}</span>` },
1324
+ { key: "owner", label: "Owner", mono: true, render: (v, r) => `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._owner}'">${short(v)}</span>` },
1325
+ { key: "time", label: "Time" },
1326
+ ],
1327
+ rows,
1328
+ (row) => navigate("#/entity/" + row._id),
1329
+ ),
1330
+ ))
1331
+ } else {
1332
+ el.appendChild(section("Processes", document.createTextNode("No processes using this module")))
1333
+ }
1334
+ } catch {
1335
+ // ok
1336
+ }
1337
+ }
1338
+
1339
+ // ---- Block ----
1340
+
1341
+ async function renderBlock(el, height) {
1342
+ const blocks = await queryBlocks({ first: 1, min: height, max: height })
1343
+ const edge = blocks?.edges?.[0]
1344
+ if (!edge) {
1345
+ el.innerHTML = `<div class="loading">Block ${height} not found</div>`
1346
+ return
1347
+ }
1348
+ await renderBlockNode(el, edge.node)
1349
+ }
1350
+
1351
+ async function renderBlockNode(el, node) {
1352
+ el.innerHTML = ""
1353
+ el.appendChild(detailHeader("Block", String(node.height), `Block #${node.height}`))
1354
+
1355
+ el.appendChild(detailFields([
1356
+ ["Block ID", node.id],
1357
+ ["Height", node.height],
1358
+ ["Timestamp", formatDate(node.timestamp)],
1359
+ ["Previous", node.previous ? link(node.previous, node.previous) : "\u2014"],
1360
+ ]))
1361
+
1362
+ try {
1363
+ const txs = await queryTxs({ first: 100, sort: "HEIGHT_DESC" })
1364
+ const blockTxs = (txs?.edges || []).filter((e) => e.node.block?.height === node.height && !e.node.bundledIn?.id)
1365
+ if (blockTxs.length) {
1366
+ const rows = blockTxs.map((e) => {
1367
+ const tags = tagsMap(e.node.tags)
1368
+ return {
1369
+ type: tags["Bundle-Format"] ? "Bundle" : tags["Type"] || contentTypeLabel(e.node.data?.type || tags["Content-Type"]) || "TX",
1370
+ id: e.node.id,
1371
+ owner: e.node.owner?.address,
1372
+ _id: e.node.id,
1373
+ _owner: e.node.owner?.address || "",
1374
+ }
1375
+ })
1376
+ el.appendChild(section(
1377
+ `Transactions (${blockTxs.length})`,
1378
+ table(
1379
+ [
1380
+ { key: "type", label: "Type", render: (v) => typeBadgeHtml(v) },
1381
+ { key: "id", label: "ID", mono: true, render: (v) => `<span class="link">${short(v)}</span>` },
1382
+ { key: "owner", label: "Owner", mono: true, render: (v, r) => `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._owner}'">${short(v)}</span>` },
1383
+ ],
1384
+ rows,
1385
+ (row) => navigate("#/entity/" + row._id),
1386
+ ),
1387
+ ))
1388
+ }
1389
+ } catch {
1390
+ // ok
1391
+ }
1392
+ }
1393
+
1394
+ // ---- Address ----
1395
+
1396
+ async function renderAddress(el, addr, txs) {
1397
+ el.innerHTML = ""
1398
+ el.appendChild(detailHeader("Address", addr))
1399
+
1400
+ const rows = txs.edges.map((e) => {
1401
+ const tags = tagsMap(e.node.tags)
1402
+ return {
1403
+ type: tags["Type"] || contentTypeLabel(e.node.data?.type || tags["Content-Type"]) || "TX",
1404
+ id: e.node.id,
1405
+ recipient: e.node.recipient || "\u2014",
1406
+ time: fromNow(e.node.block?.timestamp),
1407
+ _id: e.node.id,
1408
+ _recipient: e.node.recipient || "",
1409
+ }
1410
+ })
1411
+ el.appendChild(section(
1412
+ `Transactions (${txs.edges.length})`,
1413
+ table(
1414
+ [
1415
+ { key: "type", label: "Type", render: (v) => typeBadgeHtml(v) },
1416
+ { key: "id", label: "ID", mono: true, render: (v) => `<span class="link">${short(v)}</span>` },
1417
+ { key: "recipient", label: "Recipient", mono: true, render: (v, r) => r._recipient ? `<span class="link" onclick="event.stopPropagation();location.hash='#/entity/${r._recipient}'">${short(v)}</span>` : v },
1418
+ { key: "time", label: "Timestamp" },
1419
+ ],
1420
+ rows,
1421
+ (row) => navigate("#/entity/" + row._id),
1422
+ ),
1423
+ ))
1424
+
1425
+ // Also show messages sent by this address
1426
+ try {
1427
+ const msgs = await queryTxs({
1428
+ first: 50,
1429
+ owners: [addr],
1430
+ tags: [{ name: "Type", values: ["Message"] }],
1431
+ sort: "HEIGHT_DESC",
1432
+ })
1433
+ const msgEdges = msgs?.edges || []
1434
+ if (msgEdges.length) {
1435
+ el.appendChild(section(
1436
+ `Messages Sent (${msgEdges.length})`,
1437
+ msgTable(msgEdges),
1438
+ ))
1439
+ }
1440
+ } catch {
1441
+ // ok
1442
+ }
1443
+ }
1444
+
1445
+ // ---- Generic ----
1446
+
1447
+ async function renderGeneric(el, node, tags, type) {
1448
+ el.innerHTML = ""
1449
+ const header = detailHeader(type, node.id)
1450
+ const fields = detailFields([
1451
+ ["Owner", link(node.owner?.address, node.owner?.address)],
1452
+ ["Recipient", node.recipient ? link(node.recipient, node.recipient) : "\u2014"],
1453
+ ["Block", node.block?.height ?? "\u2014"],
1454
+ ["Timestamp", formatDate(node.block?.timestamp)],
1455
+ ["Data Size", node.data?.size ? node.data.size + " bytes" : "\u2014"],
1456
+ ])
1457
+ el.appendChild(twoCol(header, fields, section("Tags", renderTags(node.tags))))
1458
+
1459
+ // For bundles, show item list instead of raw binary
1460
+ if (tags["Bundle-Format"]) {
1461
+ const bundleSection = document.createElement("div")
1462
+ bundleSection.className = "section"
1463
+ bundleSection.innerHTML = '<div class="section-title">Bundle Items</div>'
1464
+ el.appendChild(bundleSection)
1465
+ await renderBundleItems(bundleSection, node.id)
1466
+ } else {
1467
+ const dataSection = document.createElement("div")
1468
+ dataSection.className = "section"
1469
+ dataSection.innerHTML = '<div class="section-title">Data</div>'
1470
+ el.appendChild(dataSection)
1471
+ renderDataViewer(dataSection, node.id, null, node)
1472
+ }
1473
+ }