opendevbrowser 0.0.15 → 0.0.17

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 (646) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +262 -43
  3. package/dist/annotate/direct-annotator.d.ts +22 -0
  4. package/dist/annotate/direct-annotator.d.ts.map +1 -0
  5. package/dist/annotate/output.d.ts +10 -0
  6. package/dist/annotate/output.d.ts.map +1 -0
  7. package/dist/browser/annotation-manager.d.ts +33 -0
  8. package/dist/browser/annotation-manager.d.ts.map +1 -0
  9. package/dist/browser/browser-manager.d.ts +402 -0
  10. package/dist/browser/browser-manager.d.ts.map +1 -0
  11. package/dist/browser/canvas-client.d.ts +53 -0
  12. package/dist/browser/canvas-client.d.ts.map +1 -0
  13. package/dist/browser/canvas-code-sync-manager.d.ts +79 -0
  14. package/dist/browser/canvas-code-sync-manager.d.ts.map +1 -0
  15. package/dist/browser/canvas-manager.d.ts +94 -0
  16. package/dist/browser/canvas-manager.d.ts.map +1 -0
  17. package/dist/browser/canvas-runtime-preview-bridge.d.ts +20 -0
  18. package/dist/browser/canvas-runtime-preview-bridge.d.ts.map +1 -0
  19. package/dist/browser/canvas-session-sync-manager.d.ts +21 -0
  20. package/dist/browser/canvas-session-sync-manager.d.ts.map +1 -0
  21. package/dist/browser/fingerprint/adapters.d.ts +26 -0
  22. package/dist/browser/fingerprint/adapters.d.ts.map +1 -0
  23. package/dist/browser/fingerprint/canary.d.ts +25 -0
  24. package/dist/browser/fingerprint/canary.d.ts.map +1 -0
  25. package/dist/browser/fingerprint/profiles.d.ts +16 -0
  26. package/dist/browser/fingerprint/profiles.d.ts.map +1 -0
  27. package/dist/browser/fingerprint/tier1-coherence.d.ts +36 -0
  28. package/dist/browser/fingerprint/tier1-coherence.d.ts.map +1 -0
  29. package/dist/browser/fingerprint/tier2-runtime.d.ts +40 -0
  30. package/dist/browser/fingerprint/tier2-runtime.d.ts.map +1 -0
  31. package/dist/browser/fingerprint/tier3-adaptive.d.ts +30 -0
  32. package/dist/browser/fingerprint/tier3-adaptive.d.ts.map +1 -0
  33. package/dist/browser/manager-types.d.ts +15 -0
  34. package/dist/browser/manager-types.d.ts.map +1 -0
  35. package/dist/browser/ops-browser-manager.d.ts +141 -0
  36. package/dist/browser/ops-browser-manager.d.ts.map +1 -0
  37. package/dist/browser/ops-client.d.ts +56 -0
  38. package/dist/browser/ops-client.d.ts.map +1 -0
  39. package/dist/browser/parallelism-governor.d.ts +31 -0
  40. package/dist/browser/parallelism-governor.d.ts.map +1 -0
  41. package/dist/browser/script-runner.d.ts +23 -0
  42. package/dist/browser/script-runner.d.ts.map +1 -0
  43. package/dist/browser/session-store.d.ts +63 -0
  44. package/dist/browser/session-store.d.ts.map +1 -0
  45. package/dist/browser/target-manager.d.ts +36 -0
  46. package/dist/browser/target-manager.d.ts.map +1 -0
  47. package/dist/cache/chrome-locator.d.ts +2 -0
  48. package/dist/cache/chrome-locator.d.ts.map +1 -0
  49. package/dist/cache/downloader.d.ts +6 -0
  50. package/dist/cache/downloader.d.ts.map +1 -0
  51. package/dist/cache/paths.d.ts +9 -0
  52. package/dist/cache/paths.d.ts.map +1 -0
  53. package/dist/canvas/code-sync/apply-tsx.d.ts +23 -0
  54. package/dist/canvas/code-sync/apply-tsx.d.ts.map +1 -0
  55. package/dist/canvas/code-sync/graph.d.ts +5 -0
  56. package/dist/canvas/code-sync/graph.d.ts.map +1 -0
  57. package/dist/canvas/code-sync/hash.d.ts +3 -0
  58. package/dist/canvas/code-sync/hash.d.ts.map +1 -0
  59. package/dist/canvas/code-sync/import.d.ts +18 -0
  60. package/dist/canvas/code-sync/import.d.ts.map +1 -0
  61. package/dist/canvas/code-sync/manifest.d.ts +5 -0
  62. package/dist/canvas/code-sync/manifest.d.ts.map +1 -0
  63. package/dist/canvas/code-sync/tsx-adapter.d.ts +8 -0
  64. package/dist/canvas/code-sync/tsx-adapter.d.ts.map +1 -0
  65. package/dist/canvas/code-sync/types.d.ts +152 -0
  66. package/dist/canvas/code-sync/types.d.ts.map +1 -0
  67. package/dist/canvas/code-sync/write.d.ts +9 -0
  68. package/dist/canvas/code-sync/write.d.ts.map +1 -0
  69. package/dist/canvas/document-store.d.ts +81 -0
  70. package/dist/canvas/document-store.d.ts.map +1 -0
  71. package/dist/canvas/export.d.ts +12 -0
  72. package/dist/canvas/export.d.ts.map +1 -0
  73. package/dist/canvas/repo-store.d.ts +10 -0
  74. package/dist/canvas/repo-store.d.ts.map +1 -0
  75. package/dist/canvas/surface-palette.d.ts +15 -0
  76. package/dist/canvas/surface-palette.d.ts.map +1 -0
  77. package/dist/canvas/types.d.ts +255 -0
  78. package/dist/canvas/types.d.ts.map +1 -0
  79. package/dist/canvas-runtime-preview-bridge-HBEHXM4T.js +7 -0
  80. package/dist/canvas-runtime-preview-bridge-HBEHXM4T.js.map +1 -0
  81. package/dist/chunk-5J3IFL3X.js +16706 -0
  82. package/dist/chunk-5J3IFL3X.js.map +1 -0
  83. package/dist/chunk-D633UO34.js +8149 -0
  84. package/dist/chunk-D633UO34.js.map +1 -0
  85. package/dist/chunk-FUSXMW3G.js +169 -0
  86. package/dist/chunk-FUSXMW3G.js.map +1 -0
  87. package/dist/chunk-TBUCZX4A.js +34 -0
  88. package/dist/chunk-TBUCZX4A.js.map +1 -0
  89. package/dist/chunk-V7KUDHDG.js +276 -0
  90. package/dist/chunk-V7KUDHDG.js.map +1 -0
  91. package/dist/chunk-Y2KL55OG.js +59 -0
  92. package/dist/chunk-Y2KL55OG.js.map +1 -0
  93. package/dist/cli/args.d.ts +25 -0
  94. package/dist/cli/args.d.ts.map +1 -0
  95. package/dist/cli/client.d.ts +2 -0
  96. package/dist/cli/client.d.ts.map +1 -0
  97. package/dist/cli/commands/annotate.d.ts +38 -0
  98. package/dist/cli/commands/annotate.d.ts.map +1 -0
  99. package/dist/cli/commands/artifacts.d.ts +24 -0
  100. package/dist/cli/commands/artifacts.d.ts.map +1 -0
  101. package/dist/cli/commands/canvas.d.ts +45 -0
  102. package/dist/cli/commands/canvas.d.ts.map +1 -0
  103. package/dist/cli/commands/daemon.d.ts +35 -0
  104. package/dist/cli/commands/daemon.d.ts.map +1 -0
  105. package/dist/cli/commands/devtools/console-poll.d.ts +7 -0
  106. package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -0
  107. package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts +20 -0
  108. package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts.map +1 -0
  109. package/dist/cli/commands/devtools/network-poll.d.ts +7 -0
  110. package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -0
  111. package/dist/cli/commands/devtools/perf.d.ts +7 -0
  112. package/dist/cli/commands/devtools/perf.d.ts.map +1 -0
  113. package/dist/cli/commands/devtools/screenshot.d.ts +18 -0
  114. package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -0
  115. package/dist/cli/commands/dom/attr.d.ts +7 -0
  116. package/dist/cli/commands/dom/attr.d.ts.map +1 -0
  117. package/dist/cli/commands/dom/checked.d.ts +7 -0
  118. package/dist/cli/commands/dom/checked.d.ts.map +1 -0
  119. package/dist/cli/commands/dom/enabled.d.ts +7 -0
  120. package/dist/cli/commands/dom/enabled.d.ts.map +1 -0
  121. package/dist/cli/commands/dom/html.d.ts +7 -0
  122. package/dist/cli/commands/dom/html.d.ts.map +1 -0
  123. package/dist/cli/commands/dom/text.d.ts +7 -0
  124. package/dist/cli/commands/dom/text.d.ts.map +1 -0
  125. package/dist/cli/commands/dom/value.d.ts +7 -0
  126. package/dist/cli/commands/dom/value.d.ts.map +1 -0
  127. package/dist/cli/commands/dom/visible.d.ts +7 -0
  128. package/dist/cli/commands/dom/visible.d.ts.map +1 -0
  129. package/dist/cli/commands/export/clone-component.d.ts +16 -0
  130. package/dist/cli/commands/export/clone-component.d.ts.map +1 -0
  131. package/dist/cli/commands/export/clone-page.d.ts +15 -0
  132. package/dist/cli/commands/export/clone-page.d.ts.map +1 -0
  133. package/dist/cli/commands/interact/check.d.ts +7 -0
  134. package/dist/cli/commands/interact/check.d.ts.map +1 -0
  135. package/dist/cli/commands/interact/click.d.ts +7 -0
  136. package/dist/cli/commands/interact/click.d.ts.map +1 -0
  137. package/dist/cli/commands/interact/hover.d.ts +7 -0
  138. package/dist/cli/commands/interact/hover.d.ts.map +1 -0
  139. package/dist/cli/commands/interact/press.d.ts +7 -0
  140. package/dist/cli/commands/interact/press.d.ts.map +1 -0
  141. package/dist/cli/commands/interact/scroll-into-view.d.ts +7 -0
  142. package/dist/cli/commands/interact/scroll-into-view.d.ts.map +1 -0
  143. package/dist/cli/commands/interact/scroll.d.ts +7 -0
  144. package/dist/cli/commands/interact/scroll.d.ts.map +1 -0
  145. package/dist/cli/commands/interact/select.d.ts +7 -0
  146. package/dist/cli/commands/interact/select.d.ts.map +1 -0
  147. package/dist/cli/commands/interact/type.d.ts +7 -0
  148. package/dist/cli/commands/interact/type.d.ts.map +1 -0
  149. package/dist/cli/commands/interact/uncheck.d.ts +7 -0
  150. package/dist/cli/commands/interact/uncheck.d.ts.map +1 -0
  151. package/dist/cli/commands/macro-resolve.d.ts +18 -0
  152. package/dist/cli/commands/macro-resolve.d.ts.map +1 -0
  153. package/dist/cli/commands/native.d.ts +93 -0
  154. package/dist/cli/commands/native.d.ts.map +1 -0
  155. package/dist/cli/commands/nav/goto.d.ts +7 -0
  156. package/dist/cli/commands/nav/goto.d.ts.map +1 -0
  157. package/dist/cli/commands/nav/snapshot.d.ts +7 -0
  158. package/dist/cli/commands/nav/snapshot.d.ts.map +1 -0
  159. package/dist/cli/commands/nav/wait.d.ts +7 -0
  160. package/dist/cli/commands/nav/wait.d.ts.map +1 -0
  161. package/dist/cli/commands/pages/close.d.ts +6 -0
  162. package/dist/cli/commands/pages/close.d.ts.map +1 -0
  163. package/dist/cli/commands/pages/list.d.ts +7 -0
  164. package/dist/cli/commands/pages/list.d.ts.map +1 -0
  165. package/dist/cli/commands/pages/open.d.ts +7 -0
  166. package/dist/cli/commands/pages/open.d.ts.map +1 -0
  167. package/dist/cli/commands/product-video.d.ts +25 -0
  168. package/dist/cli/commands/product-video.d.ts.map +1 -0
  169. package/dist/cli/commands/registry.d.ts +5 -0
  170. package/dist/cli/commands/registry.d.ts.map +1 -0
  171. package/dist/cli/commands/research.d.ts +27 -0
  172. package/dist/cli/commands/research.d.ts.map +1 -0
  173. package/dist/cli/commands/rpc.d.ts +28 -0
  174. package/dist/cli/commands/rpc.d.ts.map +1 -0
  175. package/dist/cli/commands/run.d.ts +17 -0
  176. package/dist/cli/commands/run.d.ts.map +1 -0
  177. package/dist/cli/commands/serve.d.ts +64 -0
  178. package/dist/cli/commands/serve.d.ts.map +1 -0
  179. package/dist/cli/commands/session/connect.d.ts +9 -0
  180. package/dist/cli/commands/session/connect.d.ts.map +1 -0
  181. package/dist/cli/commands/session/cookie-import.d.ts +31 -0
  182. package/dist/cli/commands/session/cookie-import.d.ts.map +1 -0
  183. package/dist/cli/commands/session/cookie-list.d.ts +17 -0
  184. package/dist/cli/commands/session/cookie-list.d.ts.map +1 -0
  185. package/dist/cli/commands/session/disconnect.d.ts +6 -0
  186. package/dist/cli/commands/session/disconnect.d.ts.map +1 -0
  187. package/dist/cli/commands/session/launch.d.ts +29 -0
  188. package/dist/cli/commands/session/launch.d.ts.map +1 -0
  189. package/dist/cli/commands/session/status.d.ts +7 -0
  190. package/dist/cli/commands/session/status.d.ts.map +1 -0
  191. package/dist/cli/commands/shopping.d.ts +25 -0
  192. package/dist/cli/commands/shopping.d.ts.map +1 -0
  193. package/dist/cli/commands/status.d.ts +24 -0
  194. package/dist/cli/commands/status.d.ts.map +1 -0
  195. package/dist/cli/commands/targets/close.d.ts +6 -0
  196. package/dist/cli/commands/targets/close.d.ts.map +1 -0
  197. package/dist/cli/commands/targets/list.d.ts +7 -0
  198. package/dist/cli/commands/targets/list.d.ts.map +1 -0
  199. package/dist/cli/commands/targets/new.d.ts +7 -0
  200. package/dist/cli/commands/targets/new.d.ts.map +1 -0
  201. package/dist/cli/commands/targets/use.d.ts +7 -0
  202. package/dist/cli/commands/targets/use.d.ts.map +1 -0
  203. package/dist/cli/commands/types.d.ts +13 -0
  204. package/dist/cli/commands/types.d.ts.map +1 -0
  205. package/dist/cli/commands/uninstall.d.ts +14 -0
  206. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  207. package/dist/cli/commands/update.d.ts +7 -0
  208. package/dist/cli/commands/update.d.ts.map +1 -0
  209. package/dist/cli/daemon-autostart.d.ts +46 -0
  210. package/dist/cli/daemon-autostart.d.ts.map +1 -0
  211. package/dist/cli/daemon-client.d.ts +33 -0
  212. package/dist/cli/daemon-client.d.ts.map +1 -0
  213. package/dist/cli/daemon-commands.d.ts +7 -0
  214. package/dist/cli/daemon-commands.d.ts.map +1 -0
  215. package/dist/cli/daemon-state.d.ts +56 -0
  216. package/dist/cli/daemon-state.d.ts.map +1 -0
  217. package/dist/cli/daemon-status.d.ts +19 -0
  218. package/dist/cli/daemon-status.d.ts.map +1 -0
  219. package/dist/cli/daemon.d.ts +29 -0
  220. package/dist/cli/daemon.d.ts.map +1 -0
  221. package/dist/cli/errors.d.ts +20 -0
  222. package/dist/cli/errors.d.ts.map +1 -0
  223. package/dist/cli/help.d.ts +33 -0
  224. package/dist/cli/help.d.ts.map +1 -0
  225. package/dist/cli/index.d.ts +2 -0
  226. package/dist/cli/index.d.ts.map +1 -0
  227. package/dist/cli/index.js +2825 -326
  228. package/dist/cli/index.js.map +1 -1
  229. package/dist/cli/installers/global.d.ts +9 -0
  230. package/dist/cli/installers/global.d.ts.map +1 -0
  231. package/dist/cli/installers/local.d.ts +9 -0
  232. package/dist/cli/installers/local.d.ts.map +1 -0
  233. package/dist/cli/installers/skills.d.ts +19 -0
  234. package/dist/cli/installers/skills.d.ts.map +1 -0
  235. package/dist/cli/output.d.ts +7 -0
  236. package/dist/cli/output.d.ts.map +1 -0
  237. package/dist/cli/remote-canvas-manager.d.ts +8 -0
  238. package/dist/cli/remote-canvas-manager.d.ts.map +1 -0
  239. package/dist/cli/remote-manager.d.ts +98 -0
  240. package/dist/cli/remote-manager.d.ts.map +1 -0
  241. package/dist/cli/remote-relay.d.ts +19 -0
  242. package/dist/cli/remote-relay.d.ts.map +1 -0
  243. package/dist/cli/templates/config.d.ts +7 -0
  244. package/dist/cli/templates/config.d.ts.map +1 -0
  245. package/dist/cli/utils/config.d.ts +20 -0
  246. package/dist/cli/utils/config.d.ts.map +1 -0
  247. package/dist/cli/utils/http.d.ts +5 -0
  248. package/dist/cli/utils/http.d.ts.map +1 -0
  249. package/dist/cli/utils/parse.d.ts +9 -0
  250. package/dist/cli/utils/parse.d.ts.map +1 -0
  251. package/dist/cli/utils/skills.d.ts +12 -0
  252. package/dist/cli/utils/skills.d.ts.map +1 -0
  253. package/dist/config.d.ts +208 -0
  254. package/dist/config.d.ts.map +1 -0
  255. package/dist/core/bootstrap.d.ts +3 -0
  256. package/dist/core/bootstrap.d.ts.map +1 -0
  257. package/dist/core/index.d.ts +3 -0
  258. package/dist/core/index.d.ts.map +1 -0
  259. package/dist/core/logging.d.ts +34 -0
  260. package/dist/core/logging.d.ts.map +1 -0
  261. package/dist/core/types.d.ts +36 -0
  262. package/dist/core/types.d.ts.map +1 -0
  263. package/dist/devtools/console-tracker.d.ts +44 -0
  264. package/dist/devtools/console-tracker.d.ts.map +1 -0
  265. package/dist/devtools/exception-tracker.d.ts +42 -0
  266. package/dist/devtools/exception-tracker.d.ts.map +1 -0
  267. package/dist/devtools/network-tracker.d.ts +34 -0
  268. package/dist/devtools/network-tracker.d.ts.map +1 -0
  269. package/dist/export/css-extract.d.ts +5 -0
  270. package/dist/export/css-extract.d.ts.map +1 -0
  271. package/dist/export/dom-capture.d.ts +15 -0
  272. package/dist/export/dom-capture.d.ts.map +1 -0
  273. package/dist/export/react-emitter.d.ts +11 -0
  274. package/dist/export/react-emitter.d.ts.map +1 -0
  275. package/dist/extension-extractor.d.ts +3 -0
  276. package/dist/extension-extractor.d.ts.map +1 -0
  277. package/dist/fs-UMRKOBNN.js +7 -0
  278. package/dist/fs-UMRKOBNN.js.map +1 -0
  279. package/dist/index.d.ts +3 -4
  280. package/dist/index.d.ts.map +1 -0
  281. package/dist/index.js +1028 -123
  282. package/dist/index.js.map +1 -1
  283. package/dist/macros/execute.d.ts +44 -0
  284. package/dist/macros/execute.d.ts.map +1 -0
  285. package/dist/macros/index.d.ts +9 -0
  286. package/dist/macros/index.d.ts.map +1 -0
  287. package/dist/macros/packs/core.d.ts +3 -0
  288. package/dist/macros/packs/core.d.ts.map +1 -0
  289. package/dist/macros/registry.d.ts +48 -0
  290. package/dist/macros/registry.d.ts.map +1 -0
  291. package/dist/macros-ND2M7LWU.js +399 -0
  292. package/dist/macros-ND2M7LWU.js.map +1 -0
  293. package/dist/opendevbrowser.d.ts +3 -4
  294. package/dist/opendevbrowser.d.ts.map +1 -0
  295. package/dist/opendevbrowser.js +1028 -123
  296. package/dist/opendevbrowser.js.map +1 -1
  297. package/dist/providers/adaptive-concurrency.d.ts +42 -0
  298. package/dist/providers/adaptive-concurrency.d.ts.map +1 -0
  299. package/dist/providers/artifacts.d.ts +34 -0
  300. package/dist/providers/artifacts.d.ts.map +1 -0
  301. package/dist/providers/blocker.d.ts +47 -0
  302. package/dist/providers/blocker.d.ts.map +1 -0
  303. package/dist/providers/community/index.d.ts +44 -0
  304. package/dist/providers/community/index.d.ts.map +1 -0
  305. package/dist/providers/enrichment.d.ts +33 -0
  306. package/dist/providers/enrichment.d.ts.map +1 -0
  307. package/dist/providers/errors.d.ts +41 -0
  308. package/dist/providers/errors.d.ts.map +1 -0
  309. package/dist/providers/index.d.ts +121 -0
  310. package/dist/providers/index.d.ts.map +1 -0
  311. package/dist/providers/normalize.d.ts +39 -0
  312. package/dist/providers/normalize.d.ts.map +1 -0
  313. package/dist/providers/policy.d.ts +5 -0
  314. package/dist/providers/policy.d.ts.map +1 -0
  315. package/dist/providers/registry.d.ts +22 -0
  316. package/dist/providers/registry.d.ts.map +1 -0
  317. package/dist/providers/renderer.d.ts +49 -0
  318. package/dist/providers/renderer.d.ts.map +1 -0
  319. package/dist/providers/runtime-factory.d.ts +20 -0
  320. package/dist/providers/runtime-factory.d.ts.map +1 -0
  321. package/dist/providers/safety/prompt-guard.d.ts +34 -0
  322. package/dist/providers/safety/prompt-guard.d.ts.map +1 -0
  323. package/dist/providers/shared/anti-bot-policy.d.ts +51 -0
  324. package/dist/providers/shared/anti-bot-policy.d.ts.map +1 -0
  325. package/dist/providers/shared/post-policy.d.ts +31 -0
  326. package/dist/providers/shared/post-policy.d.ts.map +1 -0
  327. package/dist/providers/shared/request-headers.d.ts +5 -0
  328. package/dist/providers/shared/request-headers.d.ts.map +1 -0
  329. package/dist/providers/shared/traversal-url.d.ts +2 -0
  330. package/dist/providers/shared/traversal-url.d.ts.map +1 -0
  331. package/dist/providers/shopping/index.d.ts +63 -0
  332. package/dist/providers/shopping/index.d.ts.map +1 -0
  333. package/dist/providers/social/bluesky.d.ts +3 -0
  334. package/dist/providers/social/bluesky.d.ts.map +1 -0
  335. package/dist/providers/social/facebook.d.ts +3 -0
  336. package/dist/providers/social/facebook.d.ts.map +1 -0
  337. package/dist/providers/social/index.d.ts +31 -0
  338. package/dist/providers/social/index.d.ts.map +1 -0
  339. package/dist/providers/social/instagram.d.ts +3 -0
  340. package/dist/providers/social/instagram.d.ts.map +1 -0
  341. package/dist/providers/social/linkedin.d.ts +3 -0
  342. package/dist/providers/social/linkedin.d.ts.map +1 -0
  343. package/dist/providers/social/platform.d.ts +40 -0
  344. package/dist/providers/social/platform.d.ts.map +1 -0
  345. package/dist/providers/social/reddit.d.ts +3 -0
  346. package/dist/providers/social/reddit.d.ts.map +1 -0
  347. package/dist/providers/social/threads.d.ts +3 -0
  348. package/dist/providers/social/threads.d.ts.map +1 -0
  349. package/dist/providers/social/tiktok.d.ts +3 -0
  350. package/dist/providers/social/tiktok.d.ts.map +1 -0
  351. package/dist/providers/social/x.d.ts +3 -0
  352. package/dist/providers/social/x.d.ts.map +1 -0
  353. package/dist/providers/social/youtube-resolver.d.ts +78 -0
  354. package/dist/providers/social/youtube-resolver.d.ts.map +1 -0
  355. package/dist/providers/social/youtube.d.ts +34 -0
  356. package/dist/providers/social/youtube.d.ts.map +1 -0
  357. package/dist/providers/tier-router.d.ts +30 -0
  358. package/dist/providers/tier-router.d.ts.map +1 -0
  359. package/dist/providers/timebox.d.ts +20 -0
  360. package/dist/providers/timebox.d.ts.map +1 -0
  361. package/dist/providers/types.d.ts +344 -0
  362. package/dist/providers/types.d.ts.map +1 -0
  363. package/dist/providers/web/crawl-worker.d.ts +36 -0
  364. package/dist/providers/web/crawl-worker.d.ts.map +1 -0
  365. package/dist/providers/web/crawler.d.ts +101 -0
  366. package/dist/providers/web/crawler.d.ts.map +1 -0
  367. package/dist/providers/web/extract.d.ts +11 -0
  368. package/dist/providers/web/extract.d.ts.map +1 -0
  369. package/dist/providers/web/index.d.ts +24 -0
  370. package/dist/providers/web/index.d.ts.map +1 -0
  371. package/dist/providers/web/policy.d.ts +14 -0
  372. package/dist/providers/web/policy.d.ts.map +1 -0
  373. package/dist/providers/workflows.d.ts +67 -0
  374. package/dist/providers/workflows.d.ts.map +1 -0
  375. package/dist/providers-G3LRHQXX.js +121 -0
  376. package/dist/providers-G3LRHQXX.js.map +1 -0
  377. package/dist/relay/protocol.d.ts +399 -0
  378. package/dist/relay/protocol.d.ts.map +1 -0
  379. package/dist/relay/relay-endpoints.d.ts +16 -0
  380. package/dist/relay/relay-endpoints.d.ts.map +1 -0
  381. package/dist/relay/relay-server.d.ts +124 -0
  382. package/dist/relay/relay-server.d.ts.map +1 -0
  383. package/dist/relay/relay-types.d.ts +12 -0
  384. package/dist/relay/relay-types.d.ts.map +1 -0
  385. package/dist/runtime-factory-BICHDPE7.js +13 -0
  386. package/dist/runtime-factory-BICHDPE7.js.map +1 -0
  387. package/dist/skills/continuity-nudge.d.ts +12 -0
  388. package/dist/skills/continuity-nudge.d.ts.map +1 -0
  389. package/dist/skills/skill-loader.d.ts +20 -0
  390. package/dist/skills/skill-loader.d.ts.map +1 -0
  391. package/dist/skills/skill-nudge.d.ts +18 -0
  392. package/dist/skills/skill-nudge.d.ts.map +1 -0
  393. package/dist/skills/types.d.ts +12 -0
  394. package/dist/skills/types.d.ts.map +1 -0
  395. package/dist/snapshot/ops-snapshot.d.ts +16 -0
  396. package/dist/snapshot/ops-snapshot.d.ts.map +1 -0
  397. package/dist/snapshot/refs.d.ts +23 -0
  398. package/dist/snapshot/refs.d.ts.map +1 -0
  399. package/dist/snapshot/snapshotter.d.ts +27 -0
  400. package/dist/snapshot/snapshotter.d.ts.map +1 -0
  401. package/dist/tools/annotate.d.ts +4 -0
  402. package/dist/tools/annotate.d.ts.map +1 -0
  403. package/dist/tools/canvas.d.ts +4 -0
  404. package/dist/tools/canvas.d.ts.map +1 -0
  405. package/dist/tools/check.d.ts +4 -0
  406. package/dist/tools/check.d.ts.map +1 -0
  407. package/dist/tools/click.d.ts +4 -0
  408. package/dist/tools/click.d.ts.map +1 -0
  409. package/dist/tools/clone_component.d.ts +4 -0
  410. package/dist/tools/clone_component.d.ts.map +1 -0
  411. package/dist/tools/clone_page.d.ts +4 -0
  412. package/dist/tools/clone_page.d.ts.map +1 -0
  413. package/dist/tools/close.d.ts +4 -0
  414. package/dist/tools/close.d.ts.map +1 -0
  415. package/dist/tools/connect.d.ts +4 -0
  416. package/dist/tools/connect.d.ts.map +1 -0
  417. package/dist/tools/console_poll.d.ts +4 -0
  418. package/dist/tools/console_poll.d.ts.map +1 -0
  419. package/dist/tools/cookie_import.d.ts +25 -0
  420. package/dist/tools/cookie_import.d.ts.map +1 -0
  421. package/dist/tools/cookie_list.d.ts +9 -0
  422. package/dist/tools/cookie_list.d.ts.map +1 -0
  423. package/dist/tools/debug_trace_snapshot.d.ts +4 -0
  424. package/dist/tools/debug_trace_snapshot.d.ts.map +1 -0
  425. package/dist/tools/deps.d.ts +28 -0
  426. package/dist/tools/deps.d.ts.map +1 -0
  427. package/dist/tools/disconnect.d.ts +4 -0
  428. package/dist/tools/disconnect.d.ts.map +1 -0
  429. package/dist/tools/dom_get_html.d.ts +4 -0
  430. package/dist/tools/dom_get_html.d.ts.map +1 -0
  431. package/dist/tools/dom_get_text.d.ts +4 -0
  432. package/dist/tools/dom_get_text.d.ts.map +1 -0
  433. package/dist/tools/get_attr.d.ts +4 -0
  434. package/dist/tools/get_attr.d.ts.map +1 -0
  435. package/dist/tools/get_value.d.ts +4 -0
  436. package/dist/tools/get_value.d.ts.map +1 -0
  437. package/dist/tools/goto.d.ts +4 -0
  438. package/dist/tools/goto.d.ts.map +1 -0
  439. package/dist/tools/hover.d.ts +4 -0
  440. package/dist/tools/hover.d.ts.map +1 -0
  441. package/dist/tools/index.d.ts +4 -0
  442. package/dist/tools/index.d.ts.map +1 -0
  443. package/dist/tools/is_checked.d.ts +4 -0
  444. package/dist/tools/is_checked.d.ts.map +1 -0
  445. package/dist/tools/is_enabled.d.ts +4 -0
  446. package/dist/tools/is_enabled.d.ts.map +1 -0
  447. package/dist/tools/is_visible.d.ts +4 -0
  448. package/dist/tools/is_visible.d.ts.map +1 -0
  449. package/dist/tools/launch.d.ts +4 -0
  450. package/dist/tools/launch.d.ts.map +1 -0
  451. package/dist/tools/list.d.ts +4 -0
  452. package/dist/tools/list.d.ts.map +1 -0
  453. package/dist/tools/macro_resolve.d.ts +25 -0
  454. package/dist/tools/macro_resolve.d.ts.map +1 -0
  455. package/dist/tools/network_poll.d.ts +4 -0
  456. package/dist/tools/network_poll.d.ts.map +1 -0
  457. package/dist/tools/page.d.ts +4 -0
  458. package/dist/tools/page.d.ts.map +1 -0
  459. package/dist/tools/perf.d.ts +4 -0
  460. package/dist/tools/perf.d.ts.map +1 -0
  461. package/dist/tools/press.d.ts +4 -0
  462. package/dist/tools/press.d.ts.map +1 -0
  463. package/dist/tools/product_video_run.d.ts +4 -0
  464. package/dist/tools/product_video_run.d.ts.map +1 -0
  465. package/dist/tools/prompting_guide.d.ts +4 -0
  466. package/dist/tools/prompting_guide.d.ts.map +1 -0
  467. package/dist/tools/research_run.d.ts +4 -0
  468. package/dist/tools/research_run.d.ts.map +1 -0
  469. package/dist/tools/response.d.ts +19 -0
  470. package/dist/tools/response.d.ts.map +1 -0
  471. package/dist/tools/run.d.ts +4 -0
  472. package/dist/tools/run.d.ts.map +1 -0
  473. package/dist/tools/screenshot.d.ts +4 -0
  474. package/dist/tools/screenshot.d.ts.map +1 -0
  475. package/dist/tools/scroll.d.ts +4 -0
  476. package/dist/tools/scroll.d.ts.map +1 -0
  477. package/dist/tools/scroll_into_view.d.ts +4 -0
  478. package/dist/tools/scroll_into_view.d.ts.map +1 -0
  479. package/dist/tools/select.d.ts +4 -0
  480. package/dist/tools/select.d.ts.map +1 -0
  481. package/dist/tools/shopping_run.d.ts +4 -0
  482. package/dist/tools/shopping_run.d.ts.map +1 -0
  483. package/dist/tools/skill_list.d.ts +4 -0
  484. package/dist/tools/skill_list.d.ts.map +1 -0
  485. package/dist/tools/skill_load.d.ts +4 -0
  486. package/dist/tools/skill_load.d.ts.map +1 -0
  487. package/dist/tools/snapshot.d.ts +4 -0
  488. package/dist/tools/snapshot.d.ts.map +1 -0
  489. package/dist/tools/status.d.ts +4 -0
  490. package/dist/tools/status.d.ts.map +1 -0
  491. package/dist/tools/target_close.d.ts +4 -0
  492. package/dist/tools/target_close.d.ts.map +1 -0
  493. package/dist/tools/target_new.d.ts +4 -0
  494. package/dist/tools/target_new.d.ts.map +1 -0
  495. package/dist/tools/target_use.d.ts +4 -0
  496. package/dist/tools/target_use.d.ts.map +1 -0
  497. package/dist/tools/targets_list.d.ts +4 -0
  498. package/dist/tools/targets_list.d.ts.map +1 -0
  499. package/dist/tools/type.d.ts +4 -0
  500. package/dist/tools/type.d.ts.map +1 -0
  501. package/dist/tools/uncheck.d.ts +4 -0
  502. package/dist/tools/uncheck.d.ts.map +1 -0
  503. package/dist/tools/wait.d.ts +4 -0
  504. package/dist/tools/wait.d.ts.map +1 -0
  505. package/dist/tools/workflow-runtime.d.ts +3 -0
  506. package/dist/tools/workflow-runtime.d.ts.map +1 -0
  507. package/dist/utils/crypto.d.ts +2 -0
  508. package/dist/utils/crypto.d.ts.map +1 -0
  509. package/dist/utils/endpoint-validation.d.ts +2 -0
  510. package/dist/utils/endpoint-validation.d.ts.map +1 -0
  511. package/dist/utils/fs.d.ts +5 -0
  512. package/dist/utils/fs.d.ts.map +1 -0
  513. package/dist/utils/hub-enabled.d.ts +3 -0
  514. package/dist/utils/hub-enabled.d.ts.map +1 -0
  515. package/extension/canvas.html +636 -0
  516. package/extension/dist/annotate-content.css +15 -6
  517. package/extension/dist/annotate-content.js +119 -9
  518. package/extension/dist/annotation-payload.js +163 -0
  519. package/extension/dist/background.js +158 -22
  520. package/extension/dist/canvas/canvas-runtime.js +1061 -0
  521. package/extension/dist/canvas/model.js +213 -0
  522. package/extension/dist/canvas/viewport-fit.js +67 -0
  523. package/extension/dist/canvas-page.js +1801 -0
  524. package/extension/dist/ops/dom-bridge.js +116 -3
  525. package/extension/dist/ops/ops-runtime.js +1014 -48
  526. package/extension/dist/ops/ops-session-store.js +37 -116
  527. package/extension/dist/ops/parallelism-governor.js +117 -0
  528. package/extension/dist/ops/snapshot-shared.js +21 -5
  529. package/extension/dist/ops/target-session-coordinator.js +157 -0
  530. package/extension/dist/popup.js +155 -31
  531. package/extension/dist/services/CDPRouter.js +70 -5
  532. package/extension/dist/services/ConnectionManager.js +22 -3
  533. package/extension/dist/services/RelayClient.js +9 -0
  534. package/extension/dist/services/TabManager.js +35 -12
  535. package/extension/dist/types.js +2 -0
  536. package/extension/icons/icon128.png +0 -0
  537. package/extension/icons/icon16.png +0 -0
  538. package/extension/icons/icon32.png +0 -0
  539. package/extension/icons/icon48.png +0 -0
  540. package/extension/manifest.json +1 -1
  541. package/extension/popup.html +52 -0
  542. package/package.json +30 -19
  543. package/scripts/native/host.cjs +230 -0
  544. package/scripts/native/install.ps1 +73 -0
  545. package/scripts/native/install.sh +66 -0
  546. package/scripts/native/uninstall.ps1 +25 -0
  547. package/scripts/native/uninstall.sh +26 -0
  548. package/skills/AGENTS.md +20 -8
  549. package/skills/opendevbrowser-best-practices/SKILL.md +248 -74
  550. package/skills/opendevbrowser-best-practices/artifacts/browser-agent-known-issues-matrix.md +44 -0
  551. package/skills/opendevbrowser-best-practices/artifacts/canvas-governance-playbook.md +141 -0
  552. package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +191 -0
  553. package/skills/opendevbrowser-best-practices/artifacts/debug-trace-playbook.md +36 -0
  554. package/skills/opendevbrowser-best-practices/artifacts/fingerprint-tiers.md +36 -0
  555. package/skills/opendevbrowser-best-practices/artifacts/macro-workflows.md +43 -0
  556. package/skills/opendevbrowser-best-practices/artifacts/parity-gates.md +36 -0
  557. package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +89 -0
  558. package/skills/opendevbrowser-best-practices/assets/templates/canvas-blocker-checklist.json +70 -0
  559. package/skills/opendevbrowser-best-practices/assets/templates/canvas-feedback-eval.json +73 -0
  560. package/skills/opendevbrowser-best-practices/assets/templates/canvas-generation-plan.v1.json +67 -0
  561. package/skills/opendevbrowser-best-practices/assets/templates/canvas-handshake-example.json +126 -0
  562. package/skills/opendevbrowser-best-practices/assets/templates/cdp-forward-envelope.json +11 -0
  563. package/skills/opendevbrowser-best-practices/assets/templates/mode-flag-matrix.json +56 -0
  564. package/skills/opendevbrowser-best-practices/assets/templates/ops-request-envelope.json +9 -0
  565. package/skills/opendevbrowser-best-practices/assets/templates/robustness-checklist.json +136 -0
  566. package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +28 -0
  567. package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +170 -0
  568. package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +164 -0
  569. package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +234 -0
  570. package/skills/opendevbrowser-continuity-ledger/SKILL.md +10 -0
  571. package/skills/opendevbrowser-data-extraction/SKILL.md +126 -0
  572. package/skills/opendevbrowser-data-extraction/artifacts/extraction-workflows.md +31 -0
  573. package/skills/opendevbrowser-data-extraction/assets/templates/compliance-checklist.md +7 -0
  574. package/skills/opendevbrowser-data-extraction/assets/templates/extraction-schema.json +17 -0
  575. package/skills/opendevbrowser-data-extraction/assets/templates/pagination-state.json +11 -0
  576. package/skills/opendevbrowser-data-extraction/assets/templates/quality-gates.json +10 -0
  577. package/skills/opendevbrowser-data-extraction/examples/sample-schema.json +19 -0
  578. package/skills/opendevbrowser-data-extraction/scripts/run-extraction-workflow.sh +83 -0
  579. package/skills/opendevbrowser-data-extraction/scripts/validate-skill-assets.sh +49 -0
  580. package/skills/opendevbrowser-form-testing/SKILL.md +143 -0
  581. package/skills/opendevbrowser-form-testing/artifacts/form-workflows.md +37 -0
  582. package/skills/opendevbrowser-form-testing/assets/templates/a11y-assertions.md +7 -0
  583. package/skills/opendevbrowser-form-testing/assets/templates/challenge-decision-tree.json +16 -0
  584. package/skills/opendevbrowser-form-testing/assets/templates/multi-step-state.json +11 -0
  585. package/skills/opendevbrowser-form-testing/assets/templates/validation-matrix.json +24 -0
  586. package/skills/opendevbrowser-form-testing/examples/sample-validation-matrix.json +29 -0
  587. package/skills/opendevbrowser-form-testing/scripts/run-form-workflow.sh +82 -0
  588. package/skills/opendevbrowser-form-testing/scripts/validate-skill-assets.sh +49 -0
  589. package/skills/opendevbrowser-login-automation/SKILL.md +159 -0
  590. package/skills/opendevbrowser-login-automation/artifacts/login-workflows.md +39 -0
  591. package/skills/opendevbrowser-login-automation/assets/templates/auth-signals.json +21 -0
  592. package/skills/opendevbrowser-login-automation/assets/templates/challenge-checkpoint.md +10 -0
  593. package/skills/opendevbrowser-login-automation/assets/templates/login-scenario-matrix.json +26 -0
  594. package/skills/opendevbrowser-login-automation/examples/sample-auth-signals.json +14 -0
  595. package/skills/opendevbrowser-login-automation/scripts/record-auth-signals.sh +18 -0
  596. package/skills/opendevbrowser-login-automation/scripts/run-login-workflow.sh +99 -0
  597. package/skills/opendevbrowser-login-automation/scripts/validate-skill-assets.sh +50 -0
  598. package/skills/opendevbrowser-product-presentation-asset/SKILL.md +98 -0
  599. package/skills/opendevbrowser-product-presentation-asset/artifacts/asset-pack-assembly.md +23 -0
  600. package/skills/opendevbrowser-product-presentation-asset/artifacts/ugc-creative-guide.md +21 -0
  601. package/skills/opendevbrowser-product-presentation-asset/assets/templates/claims-evidence-map.md +5 -0
  602. package/skills/opendevbrowser-product-presentation-asset/assets/templates/copy.md +5 -0
  603. package/skills/opendevbrowser-product-presentation-asset/assets/templates/features.md +4 -0
  604. package/skills/opendevbrowser-product-presentation-asset/assets/templates/manifest.schema.json +14 -0
  605. package/skills/opendevbrowser-product-presentation-asset/assets/templates/shot-list.md +7 -0
  606. package/skills/opendevbrowser-product-presentation-asset/assets/templates/ugc-concepts.md +17 -0
  607. package/skills/opendevbrowser-product-presentation-asset/assets/templates/user-actions.md +7 -0
  608. package/skills/opendevbrowser-product-presentation-asset/assets/templates/video-assembly.md +18 -0
  609. package/skills/opendevbrowser-product-presentation-asset/examples/sample-input.json +6 -0
  610. package/skills/opendevbrowser-product-presentation-asset/examples/sample-manifest.json +18 -0
  611. package/skills/opendevbrowser-product-presentation-asset/scripts/capture-screenshots.sh +9 -0
  612. package/skills/opendevbrowser-product-presentation-asset/scripts/collect-product.sh +14 -0
  613. package/skills/opendevbrowser-product-presentation-asset/scripts/download-images.sh +9 -0
  614. package/skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh +96 -0
  615. package/skills/opendevbrowser-product-presentation-asset/scripts/validate-skill-assets.sh +56 -0
  616. package/skills/opendevbrowser-product-presentation-asset/scripts/write-manifest.sh +43 -0
  617. package/skills/opendevbrowser-research/SKILL.md +73 -0
  618. package/skills/opendevbrowser-research/artifacts/research-workflows.md +29 -0
  619. package/skills/opendevbrowser-research/assets/templates/compact.md +7 -0
  620. package/skills/opendevbrowser-research/assets/templates/context.json +18 -0
  621. package/skills/opendevbrowser-research/assets/templates/report.md +9 -0
  622. package/skills/opendevbrowser-research/examples/sample-input.json +6 -0
  623. package/skills/opendevbrowser-research/examples/sample-output.md +4 -0
  624. package/skills/opendevbrowser-research/scripts/render-output.sh +12 -0
  625. package/skills/opendevbrowser-research/scripts/run-research.sh +23 -0
  626. package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +48 -0
  627. package/skills/opendevbrowser-research/scripts/write-artifacts.sh +29 -0
  628. package/skills/opendevbrowser-shopping/SKILL.md +118 -0
  629. package/skills/opendevbrowser-shopping/artifacts/deal-hunting-workflows.md +37 -0
  630. package/skills/opendevbrowser-shopping/assets/templates/deal-thresholds.json +8 -0
  631. package/skills/opendevbrowser-shopping/assets/templates/deals-context.json +9 -0
  632. package/skills/opendevbrowser-shopping/assets/templates/deals-table.md +4 -0
  633. package/skills/opendevbrowser-shopping/assets/templates/market-analysis.json +30 -0
  634. package/skills/opendevbrowser-shopping/examples/sample-deals.md +4 -0
  635. package/skills/opendevbrowser-shopping/examples/sample-query.json +5 -0
  636. package/skills/opendevbrowser-shopping/scripts/analyze-market.sh +307 -0
  637. package/skills/opendevbrowser-shopping/scripts/normalize-offers.sh +28 -0
  638. package/skills/opendevbrowser-shopping/scripts/render-deals.sh +13 -0
  639. package/skills/opendevbrowser-shopping/scripts/run-deal-hunt.sh +32 -0
  640. package/skills/opendevbrowser-shopping/scripts/run-shopping.sh +19 -0
  641. package/skills/opendevbrowser-shopping/scripts/validate-skill-assets.sh +53 -0
  642. package/dist/chunk-JVBMT2O5.js +0 -7173
  643. package/dist/chunk-JVBMT2O5.js.map +0 -1
  644. package/skills/data-extraction/SKILL.md +0 -128
  645. package/skills/form-testing/SKILL.md +0 -106
  646. package/skills/login-automation/SKILL.md +0 -108
@@ -0,0 +1,1801 @@
1
+ import { summarizeCanvasProjectionState, normalizeCanvasSessionSummary, normalizeCanvasTargetStateSummaries } from "./canvas/model.js";
2
+ import { buildCanvasAnnotationPayload, describeAnnotationItem } from "./annotation-payload.js";
3
+ import { DEFAULT_EDITOR_VIEWPORT, computeFittedViewport, computeViewportCanvasCenter, isDefaultEditorViewport } from "./canvas/viewport-fit.js";
4
+ const DB_NAME = "opendevbrowser-canvas";
5
+ const DB_VERSION = 2;
6
+ const STORE_NAME = "editor-state";
7
+ const CHANNEL_NAME = "opendevbrowser-canvas";
8
+ const SAVE_DEBOUNCE_MS = 180;
9
+ const UNIT_LESS_STYLES = new Set(["fontWeight", "lineHeight", "opacity", "zIndex"]);
10
+ const titleElement = requiredElement("canvas-title");
11
+ const badgesElement = requiredElement("canvas-badges");
12
+ const metaElement = requiredElement("canvas-meta");
13
+ const toolbarMetaElement = requiredElement("canvas-toolbar-meta");
14
+ const summaryElement = requiredElement("canvas-summary");
15
+ const feedbackElement = requiredElement("canvas-feedback");
16
+ const selectionMetaElement = requiredElement("canvas-selection-meta");
17
+ const stageElement = requiredElement("canvas-stage");
18
+ const stageInnerElement = requiredElement("canvas-stage-inner");
19
+ const stageMetaElement = requiredElement("canvas-stage-meta");
20
+ const previewElement = requiredElement("canvas-preview");
21
+ const emptyElement = requiredElement("canvas-empty");
22
+ const nameInput = requiredElement("canvas-node-name");
23
+ const textInput = requiredElement("canvas-node-text");
24
+ const addNoteButton = requiredElement("canvas-add-note");
25
+ const resetViewButton = requiredElement("canvas-reset-view");
26
+ const deleteNodeButton = requiredElement("canvas-delete-node");
27
+ const annotationAddButton = requiredElement("canvas-annotation-add");
28
+ const annotationCopyButton = requiredElement("canvas-annotation-copy");
29
+ const annotationSendButton = requiredElement("canvas-annotation-send");
30
+ const annotationContextInput = requiredElement("canvas-annotation-context");
31
+ const annotationListElement = requiredElement("canvas-annotation-list");
32
+ const broadcastChannel = typeof BroadcastChannel === "function" ? new BroadcastChannel(CHANNEL_NAME) : null;
33
+ const port = chrome.runtime.connect({ name: "canvas-page" });
34
+ let currentState = null;
35
+ let currentTabId = null;
36
+ let databasePromise = null;
37
+ let persistTimer = null;
38
+ let fitViewportFrame = null;
39
+ let annotationDrafts = [];
40
+ let draggingNode = null;
41
+ let panningState = null;
42
+ void bootstrap();
43
+ async function bootstrap() {
44
+ currentTabId = await getCurrentTabId();
45
+ const cached = await loadCachedState(currentTabId);
46
+ if (cached) {
47
+ applyState(cached, false);
48
+ }
49
+ port.onMessage.addListener((message) => {
50
+ handlePortMessage(message);
51
+ });
52
+ port.onDisconnect.addListener(() => {
53
+ renderMeta("Disconnected from canvas runtime");
54
+ });
55
+ broadcastChannel?.addEventListener("message", (event) => {
56
+ const state = normalizeCanvasPageState(isRecord(event.data) ? event.data.state : null);
57
+ if (!state || !shouldAcceptBroadcast(state)) {
58
+ return;
59
+ }
60
+ applyState(state, false);
61
+ });
62
+ port.postMessage({ type: "canvas-page-ready" });
63
+ bindStageInteractions();
64
+ bindInspector();
65
+ bindToolbar();
66
+ bindAnnotationPanel();
67
+ document.addEventListener("visibilitychange", () => {
68
+ if (document.visibilityState === "hidden") {
69
+ flushPersist();
70
+ }
71
+ });
72
+ window.addEventListener("beforeunload", () => {
73
+ flushPersist();
74
+ });
75
+ }
76
+ function bindToolbar() {
77
+ addNoteButton.addEventListener("click", () => {
78
+ if (!currentState || currentState.pendingMutation) {
79
+ return;
80
+ }
81
+ const page = currentState.document.pages[0];
82
+ if (!page) {
83
+ return;
84
+ }
85
+ const nodeId = `node_note_${crypto.randomUUID().slice(0, 8)}`;
86
+ const parentId = currentState.selection.nodeId ?? page.rootNodeId;
87
+ const center = computeViewportCanvasCenter(currentState.viewport, stageElement.clientWidth, stageElement.clientHeight);
88
+ const noteNode = {
89
+ id: nodeId,
90
+ kind: "note",
91
+ name: "New Note",
92
+ rect: { x: center.x - 120, y: center.y - 60, width: 240, height: 120 },
93
+ props: { text: "New note" },
94
+ style: {},
95
+ metadata: {}
96
+ };
97
+ applyOptimisticPatch([
98
+ {
99
+ op: "node.insert",
100
+ pageId: page.id,
101
+ parentId,
102
+ node: noteNode
103
+ }
104
+ ], {
105
+ pageId: page.id,
106
+ nodeId,
107
+ targetId: currentState.selection.targetId
108
+ });
109
+ });
110
+ resetViewButton.addEventListener("click", () => {
111
+ if (!currentState) {
112
+ return;
113
+ }
114
+ currentState.viewport = resolvePreferredViewport(currentState);
115
+ postViewState();
116
+ renderState();
117
+ schedulePersist(currentState);
118
+ });
119
+ deleteNodeButton.addEventListener("click", () => {
120
+ if (!currentState || currentState.pendingMutation || !currentState.selection.nodeId) {
121
+ return;
122
+ }
123
+ const nodeId = currentState.selection.nodeId;
124
+ applyOptimisticPatch([{ op: "node.remove", nodeId }], {
125
+ pageId: currentState.selection.pageId,
126
+ nodeId: null,
127
+ targetId: currentState.selection.targetId
128
+ });
129
+ });
130
+ }
131
+ function bindAnnotationPanel() {
132
+ annotationAddButton.addEventListener("click", () => {
133
+ addSelectedAnnotationDraft();
134
+ });
135
+ annotationCopyButton.addEventListener("click", () => {
136
+ void copyCanvasAnnotation(undefined, annotationCopyButton).catch((error) => {
137
+ setCanvasButtonFeedback(annotationCopyButton, "Copy failed");
138
+ console.error("[opendevbrowser canvas]", error);
139
+ });
140
+ });
141
+ annotationSendButton.addEventListener("click", () => {
142
+ void sendCanvasAnnotation(undefined, "canvas_all", "Canvas annotation payload", annotationSendButton).catch((error) => {
143
+ setCanvasButtonFeedback(annotationSendButton, "Send failed");
144
+ console.error("[opendevbrowser canvas]", error);
145
+ });
146
+ });
147
+ }
148
+ function bindInspector() {
149
+ nameInput.addEventListener("change", () => {
150
+ const node = getSelectedNode();
151
+ if (!node || !currentState || currentState.pendingMutation) {
152
+ return;
153
+ }
154
+ applyOptimisticPatch([{
155
+ op: "node.update",
156
+ nodeId: node.id,
157
+ changes: { name: nameInput.value.trim() || node.name }
158
+ }], currentState.selection);
159
+ });
160
+ textInput.addEventListener("change", () => {
161
+ const node = getSelectedNode();
162
+ if (!node || !currentState || currentState.pendingMutation) {
163
+ return;
164
+ }
165
+ applyOptimisticPatch([{
166
+ op: "node.update",
167
+ nodeId: node.id,
168
+ changes: { "props.text": textInput.value }
169
+ }], currentState.selection);
170
+ });
171
+ }
172
+ function bindStageInteractions() {
173
+ stageElement.addEventListener("pointerdown", (event) => {
174
+ const target = event.target instanceof HTMLElement ? event.target.closest("[data-node-id]") : null;
175
+ if (target) {
176
+ const nodeId = target.dataset.nodeId;
177
+ const node = nodeId ? findNode(currentState?.document ?? null, nodeId) : null;
178
+ if (!node || !currentState) {
179
+ return;
180
+ }
181
+ currentState.selection = {
182
+ pageId: node.pageId ?? currentState.document.pages[0]?.id ?? null,
183
+ nodeId: node.id,
184
+ targetId: currentState.selection.targetId,
185
+ updatedAt: new Date().toISOString()
186
+ };
187
+ draggingNode = {
188
+ nodeId: node.id,
189
+ originX: event.clientX,
190
+ originY: event.clientY,
191
+ startRectX: node.rect.x,
192
+ startRectY: node.rect.y
193
+ };
194
+ postViewState();
195
+ renderState();
196
+ stageElement.setPointerCapture(event.pointerId);
197
+ return;
198
+ }
199
+ if (!currentState) {
200
+ return;
201
+ }
202
+ panningState = {
203
+ originX: event.clientX,
204
+ originY: event.clientY,
205
+ startX: currentState.viewport.x,
206
+ startY: currentState.viewport.y
207
+ };
208
+ stageElement.dataset.mode = "panning";
209
+ stageElement.setPointerCapture(event.pointerId);
210
+ });
211
+ stageElement.addEventListener("pointermove", (event) => {
212
+ if (!currentState) {
213
+ return;
214
+ }
215
+ if (draggingNode) {
216
+ const node = findNode(currentState.document, draggingNode.nodeId);
217
+ if (!node) {
218
+ return;
219
+ }
220
+ const dx = (event.clientX - draggingNode.originX) / currentState.viewport.zoom;
221
+ const dy = (event.clientY - draggingNode.originY) / currentState.viewport.zoom;
222
+ node.rect.x = Math.round(draggingNode.startRectX + dx);
223
+ node.rect.y = Math.round(draggingNode.startRectY + dy);
224
+ renderStage();
225
+ renderSelectionMeta();
226
+ return;
227
+ }
228
+ if (panningState) {
229
+ currentState.viewport.x = Math.round(panningState.startX + (event.clientX - panningState.originX));
230
+ currentState.viewport.y = Math.round(panningState.startY + (event.clientY - panningState.originY));
231
+ renderStage();
232
+ toolbarMetaElement.textContent = formatViewport(currentState.viewport);
233
+ schedulePersist(currentState);
234
+ }
235
+ });
236
+ const endPointerInteraction = () => {
237
+ if (!currentState) {
238
+ draggingNode = null;
239
+ panningState = null;
240
+ stageElement.dataset.mode = "";
241
+ return;
242
+ }
243
+ if (draggingNode && !currentState.pendingMutation) {
244
+ const node = findNode(currentState.document, draggingNode.nodeId);
245
+ if (node) {
246
+ applyOptimisticPatch([{
247
+ op: "node.update",
248
+ nodeId: node.id,
249
+ changes: {
250
+ "rect.x": node.rect.x,
251
+ "rect.y": node.rect.y
252
+ }
253
+ }], currentState.selection, { skipOptimisticMutation: true });
254
+ }
255
+ }
256
+ else if (panningState) {
257
+ postViewState();
258
+ }
259
+ draggingNode = null;
260
+ panningState = null;
261
+ stageElement.dataset.mode = "";
262
+ };
263
+ stageElement.addEventListener("pointerup", endPointerInteraction);
264
+ stageElement.addEventListener("pointercancel", endPointerInteraction);
265
+ stageElement.addEventListener("wheel", (event) => {
266
+ if (!currentState) {
267
+ return;
268
+ }
269
+ event.preventDefault();
270
+ const nextZoom = clamp(currentState.viewport.zoom * (event.deltaY < 0 ? 1.08 : 0.92), 0.35, 2.4);
271
+ currentState.viewport.zoom = Math.round(nextZoom * 100) / 100;
272
+ renderStage();
273
+ toolbarMetaElement.textContent = formatViewport(currentState.viewport);
274
+ postViewState();
275
+ schedulePersist(currentState);
276
+ }, { passive: false });
277
+ }
278
+ function handlePortMessage(message) {
279
+ if (!message) {
280
+ return;
281
+ }
282
+ if (message.type === "canvas-page-action-request") {
283
+ const response = runCanvasPageAction(message.selector ?? null, message.action);
284
+ port.postMessage({
285
+ type: "canvas-page-action-response",
286
+ requestId: message.requestId,
287
+ ...(response.ok ? { ok: true, value: response.value } : { ok: false, error: response.error })
288
+ });
289
+ return;
290
+ }
291
+ if (message.type !== "canvas-page:init" && message.type !== "canvas-page:update" && message.type !== "canvas-page:closed") {
292
+ return;
293
+ }
294
+ if (message.type === "canvas-page:closed") {
295
+ currentState = null;
296
+ annotationDrafts = [];
297
+ previewElement.srcdoc = "";
298
+ emptyElement.hidden = false;
299
+ renderMeta(`Canvas closed${message.reason ? `: ${message.reason}` : "."}`);
300
+ stageInnerElement.innerHTML = "";
301
+ renderAnnotationPanel();
302
+ return;
303
+ }
304
+ const state = normalizeCanvasPageState(message.state);
305
+ if (!state) {
306
+ return;
307
+ }
308
+ applyState(state, true);
309
+ }
310
+ function runCanvasPageAction(selector, action) {
311
+ const resolveElement = (allowActive = false) => {
312
+ if (selector && selector.trim().length > 0) {
313
+ return document.querySelector(selector);
314
+ }
315
+ return allowActive ? document.activeElement : null;
316
+ };
317
+ const dispatchPointer = (target, type, buttons) => {
318
+ const rect = target.getBoundingClientRect();
319
+ const init = {
320
+ bubbles: true,
321
+ cancelable: true,
322
+ composed: true,
323
+ clientX: rect.left + rect.width / 2,
324
+ clientY: rect.top + rect.height / 2,
325
+ button: 0,
326
+ buttons
327
+ };
328
+ if (typeof PointerEvent === "function") {
329
+ target.dispatchEvent(new PointerEvent(type, init));
330
+ return;
331
+ }
332
+ target.dispatchEvent(new MouseEvent(type.replace(/^pointer/, "mouse"), init));
333
+ };
334
+ const dispatchMouse = (target, type, buttons) => {
335
+ const rect = target.getBoundingClientRect();
336
+ target.dispatchEvent(new MouseEvent(type, {
337
+ bubbles: true,
338
+ cancelable: true,
339
+ composed: true,
340
+ view: window,
341
+ clientX: rect.left + rect.width / 2,
342
+ clientY: rect.top + rect.height / 2,
343
+ button: 0,
344
+ buttons
345
+ }));
346
+ };
347
+ const dispatchHover = (target) => {
348
+ dispatchPointer(target, "pointerover", 0);
349
+ dispatchPointer(target, "pointerenter", 0);
350
+ dispatchMouse(target, "mouseover", 0);
351
+ dispatchMouse(target, "mouseenter", 0);
352
+ dispatchPointer(target, "pointermove", 0);
353
+ dispatchMouse(target, "mousemove", 0);
354
+ };
355
+ const dispatchClick = (target) => {
356
+ dispatchHover(target);
357
+ dispatchPointer(target, "pointerdown", 1);
358
+ dispatchMouse(target, "mousedown", 1);
359
+ if (target instanceof HTMLElement) {
360
+ target.focus();
361
+ }
362
+ dispatchPointer(target, "pointerup", 0);
363
+ dispatchMouse(target, "mouseup", 0);
364
+ if (target instanceof HTMLElement) {
365
+ target.click();
366
+ return;
367
+ }
368
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, composed: true, view: window }));
369
+ };
370
+ const selectorState = (target) => {
371
+ if (!target) {
372
+ return { attached: false, visible: false };
373
+ }
374
+ const style = window.getComputedStyle(target);
375
+ const rect = target.getBoundingClientRect();
376
+ return {
377
+ attached: true,
378
+ visible: style.display !== "none"
379
+ && style.visibility !== "hidden"
380
+ && style.opacity !== "0"
381
+ && rect.width > 0
382
+ && rect.height > 0
383
+ };
384
+ };
385
+ if (action.type === "scroll") {
386
+ const target = resolveElement(false);
387
+ if (target instanceof HTMLElement) {
388
+ target.scrollBy(0, action.dy);
389
+ return { ok: true, value: true };
390
+ }
391
+ window.scrollBy(0, action.dy);
392
+ return { ok: true, value: true };
393
+ }
394
+ if (action.type === "getSelectorState") {
395
+ return { ok: true, value: selectorState(resolveElement(false)) };
396
+ }
397
+ const target = resolveElement(action.type === "press");
398
+ if (!target) {
399
+ return { ok: false, error: "Element not found" };
400
+ }
401
+ switch (action.type) {
402
+ case "outerHTML":
403
+ return { ok: true, value: target.outerHTML };
404
+ case "innerText":
405
+ return { ok: true, value: target instanceof HTMLElement ? target.innerText || target.textContent || "" : target.textContent || "" };
406
+ case "getAttr":
407
+ return { ok: true, value: target.getAttribute(action.name) };
408
+ case "getValue":
409
+ if ("value" in target) {
410
+ return { ok: true, value: String(target.value ?? "") };
411
+ }
412
+ return { ok: true, value: null };
413
+ case "isEnabled":
414
+ if ("disabled" in target) {
415
+ return { ok: true, value: !target.disabled };
416
+ }
417
+ return { ok: true, value: true };
418
+ case "isChecked":
419
+ if ("checked" in target) {
420
+ return { ok: true, value: Boolean(target.checked) };
421
+ }
422
+ return { ok: true, value: false };
423
+ case "click":
424
+ dispatchClick(target);
425
+ return { ok: true, value: true };
426
+ case "hover":
427
+ dispatchHover(target);
428
+ return { ok: true, value: true };
429
+ case "focus":
430
+ if (target instanceof HTMLElement) {
431
+ target.focus();
432
+ }
433
+ return { ok: true, value: true };
434
+ case "press": {
435
+ if (target instanceof HTMLElement) {
436
+ target.focus();
437
+ }
438
+ const init = { key: action.key, bubbles: true, cancelable: true };
439
+ target.dispatchEvent(new KeyboardEvent("keydown", init));
440
+ target.dispatchEvent(new KeyboardEvent("keypress", init));
441
+ target.dispatchEvent(new KeyboardEvent("keyup", init));
442
+ return { ok: true, value: true };
443
+ }
444
+ case "type": {
445
+ if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) {
446
+ return { ok: false, error: "Element does not support typing" };
447
+ }
448
+ if (action.clear) {
449
+ target.value = "";
450
+ }
451
+ target.value = String(action.value ?? "");
452
+ target.dispatchEvent(new Event("input", { bubbles: true }));
453
+ target.dispatchEvent(new Event("change", { bubbles: true }));
454
+ if (action.submit) {
455
+ target.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
456
+ }
457
+ return { ok: true, value: true };
458
+ }
459
+ case "setChecked":
460
+ if (!("checked" in target)) {
461
+ return { ok: false, error: "Element does not support checked state" };
462
+ }
463
+ target.checked = Boolean(action.checked);
464
+ target.dispatchEvent(new Event("change", { bubbles: true }));
465
+ return { ok: true, value: true };
466
+ case "select":
467
+ if (!(target instanceof HTMLSelectElement)) {
468
+ return { ok: false, error: "Element is not a select" };
469
+ }
470
+ const wanted = new Set(action.values);
471
+ for (const option of Array.from(target.options)) {
472
+ option.selected = wanted.has(option.value);
473
+ }
474
+ target.dispatchEvent(new Event("input", { bubbles: true }));
475
+ target.dispatchEvent(new Event("change", { bubbles: true }));
476
+ return { ok: true, value: true };
477
+ case "scrollIntoView":
478
+ if (target instanceof HTMLElement) {
479
+ target.scrollIntoView({ block: "center", inline: "center", behavior: "auto" });
480
+ }
481
+ else {
482
+ target.scrollIntoView();
483
+ }
484
+ return { ok: true, value: true };
485
+ }
486
+ }
487
+ function applyState(state, persist) {
488
+ currentState = state;
489
+ titleElement.textContent = state.title;
490
+ renderBadges(state);
491
+ renderMeta(`Document ${state.documentId} updated ${formatTimestamp(state.updatedAt)}`);
492
+ toolbarMetaElement.textContent = formatViewport(state.viewport);
493
+ renderSummary(state.summary, state);
494
+ renderFeedback(state.feedback);
495
+ renderState();
496
+ previewElement.srcdoc = state.html;
497
+ emptyElement.hidden = Boolean(state.html);
498
+ queueViewportFitIfNeeded();
499
+ if (persist) {
500
+ schedulePersist(state);
501
+ }
502
+ }
503
+ function renderState() {
504
+ if (!currentState) {
505
+ renderAnnotationPanel();
506
+ return;
507
+ }
508
+ const projectionSummary = summarizeCanvasProjectionState(currentState.summary, currentState.targets);
509
+ const syncFragments = [
510
+ currentState.pendingMutation ? "sync pending" : "live",
511
+ currentState.summary.codeSyncState ?? null,
512
+ projectionSummary.conflictCount > 0 ? `${projectionSummary.conflictCount} conflict${projectionSummary.conflictCount === 1 ? "" : "s"}` : null
513
+ ].filter((entry) => typeof entry === "string");
514
+ stageMetaElement.textContent = `${currentState.document.pages[0]?.nodes.length ?? 0} nodes • ${syncFragments.join(" • ")}`;
515
+ renderStage();
516
+ renderInspector();
517
+ syncAnnotationDrafts();
518
+ }
519
+ function renderBadges(state) {
520
+ badgesElement.innerHTML = "";
521
+ const projectionSummary = summarizeCanvasProjectionState(state.summary, state.targets);
522
+ for (const label of [
523
+ state.previewState,
524
+ state.previewMode,
525
+ state.documentRevision === null ? "revision pending" : `revision ${state.documentRevision}`,
526
+ state.pendingMutation ? "sync pending" : "synced",
527
+ state.summary.codeSyncState,
528
+ projectionSummary.activeProjections[0],
529
+ projectionSummary.conflictCount > 0 ? `${projectionSummary.conflictCount} conflicts` : null
530
+ ]) {
531
+ if (typeof label !== "string" || label.trim().length === 0) {
532
+ continue;
533
+ }
534
+ const badge = document.createElement("span");
535
+ badge.className = "canvas-badge";
536
+ badge.textContent = label;
537
+ badgesElement.append(badge);
538
+ }
539
+ }
540
+ function renderMeta(text) {
541
+ metaElement.textContent = text;
542
+ }
543
+ function renderSummary(summary, state) {
544
+ summaryElement.innerHTML = "";
545
+ const componentLibraries = formatSummaryList(readSummaryLibraryList(summary, "components"));
546
+ const iconLibraries = formatSummaryList(readSummaryLibraryList(summary, "icons"));
547
+ const stylingLibraries = formatSummaryList(readSummaryLibraryList(summary, "styling"));
548
+ const inventorySources = formatSummaryList(readSummaryStringArray(summary.componentSourceKinds));
549
+ const projectionSummary = summarizeCanvasProjectionState(state.summary, state.targets);
550
+ const items = [
551
+ ["Target", state.targetId],
552
+ ["Session", formatSummaryValue(summary.canvasSessionId)],
553
+ ["Mode", formatSummaryValue(summary.mode)],
554
+ ["Plan", formatSummaryValue(summary.planStatus)],
555
+ ["Preflight", formatSummaryValue(summary.preflightState)],
556
+ ["Attached clients", formatAttachedClients(state.summary.attachedClients)],
557
+ ["Lease holder", state.summary.leaseHolderClientId ?? "none"],
558
+ ["Code sync", formatCodeSyncStatus(state.summary, projectionSummary.watchConflict)],
559
+ ["Projection", formatProjectionSummary(projectionSummary)],
560
+ ["Fallbacks", formatSummaryList(projectionSummary.fallbackReasons)],
561
+ ["Components", componentLibraries],
562
+ ["Icons", iconLibraries],
563
+ ["Styling", stylingLibraries],
564
+ ["Inventory", `${formatSummaryValue(summary.componentInventoryCount)} mapped`],
565
+ ["Inventory sources", inventorySources],
566
+ ["Targets", String(state.targets.length)],
567
+ ["Overlays", String(state.overlayMounts.length)],
568
+ ["Feedback", String(countFeedbackItems(state.feedback))]
569
+ ];
570
+ for (const [label, value] of items) {
571
+ const row = document.createElement("div");
572
+ row.className = "canvas-summary-item";
573
+ const title = document.createElement("div");
574
+ title.className = "canvas-summary-label";
575
+ title.textContent = label;
576
+ const body = document.createElement("div");
577
+ body.className = "canvas-summary-value";
578
+ body.textContent = value;
579
+ row.append(title, body);
580
+ summaryElement.append(row);
581
+ }
582
+ }
583
+ function renderFeedback(events) {
584
+ feedbackElement.innerHTML = "";
585
+ const latest = events.slice(-8).reverse();
586
+ for (const event of latest) {
587
+ const row = document.createElement("div");
588
+ row.className = "canvas-feedback-item";
589
+ const meta = document.createElement("div");
590
+ meta.className = "canvas-feedback-meta";
591
+ if (event.eventType === "feedback.item") {
592
+ meta.textContent = `${event.item.category} • ${event.item.severity}`;
593
+ meta.classList.add(`canvas-feedback-severity-${event.item.severity}`);
594
+ const body = document.createElement("div");
595
+ body.className = "canvas-feedback-message";
596
+ body.textContent = event.item.message;
597
+ row.append(meta, body);
598
+ }
599
+ else if (event.eventType === "feedback.heartbeat") {
600
+ meta.textContent = "heartbeat";
601
+ const body = document.createElement("div");
602
+ body.className = "canvas-feedback-message";
603
+ body.textContent = `${event.activeTargetIds.length} active targets`;
604
+ row.append(meta, body);
605
+ }
606
+ else {
607
+ meta.textContent = "stream complete";
608
+ const body = document.createElement("div");
609
+ body.className = "canvas-feedback-message";
610
+ body.textContent = event.reason;
611
+ row.append(meta, body);
612
+ }
613
+ feedbackElement.append(row);
614
+ }
615
+ }
616
+ function renderStage() {
617
+ if (!currentState) {
618
+ stageInnerElement.innerHTML = "";
619
+ return;
620
+ }
621
+ const page = currentState.document.pages[0];
622
+ if (!page) {
623
+ stageInnerElement.innerHTML = "";
624
+ return;
625
+ }
626
+ const bounds = computeDocumentBounds(page.nodes);
627
+ stageInnerElement.style.width = `${bounds.width}px`;
628
+ stageInnerElement.style.height = `${bounds.height}px`;
629
+ stageInnerElement.style.transform = `translate(${currentState.viewport.x}px, ${currentState.viewport.y}px) scale(${currentState.viewport.zoom})`;
630
+ stageInnerElement.innerHTML = "";
631
+ const sortedNodes = [...page.nodes].sort(compareStageNodes);
632
+ for (const node of sortedNodes) {
633
+ stageInnerElement.append(buildStageNodeElement(currentState.document, node, currentState.selection.nodeId === node.id));
634
+ }
635
+ }
636
+ function compareStageNodes(left, right) {
637
+ const rootOrder = Number(left.parentId !== null) - Number(right.parentId !== null);
638
+ if (rootOrder !== 0) {
639
+ return rootOrder;
640
+ }
641
+ const areaOrder = (right.rect.width * right.rect.height) - (left.rect.width * left.rect.height);
642
+ if (areaOrder !== 0) {
643
+ return areaOrder;
644
+ }
645
+ const verticalOrder = left.rect.y - right.rect.y;
646
+ return verticalOrder !== 0 ? verticalOrder : left.rect.x - right.rect.x;
647
+ }
648
+ function buildStageNodeElement(documentState, node, selected) {
649
+ const binding = resolveStageBinding(documentState, node);
650
+ const componentKind = resolveStageComponentKind(node, binding);
651
+ const media = resolveStageMediaDescriptor(documentState, node);
652
+ const text = nodeText(node);
653
+ const tag = componentKind === "button"
654
+ ? "button"
655
+ : media
656
+ ? "div"
657
+ : node.kind === "text"
658
+ ? "p"
659
+ : node.kind === "note"
660
+ ? "aside"
661
+ : "div";
662
+ const element = document.createElement(tag);
663
+ element.className = [
664
+ "canvas-node",
665
+ componentKind ? `canvas-node-${componentKind}` : "",
666
+ binding?.sourceKind ? `canvas-node-source-${binding.sourceKind.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}` : ""
667
+ ].filter(Boolean).join(" ");
668
+ element.dataset.nodeId = node.id;
669
+ element.dataset.selected = String(selected);
670
+ const accessibleLabel = describeStageNode(node, componentKind, media, text);
671
+ element.title = accessibleLabel;
672
+ element.setAttribute("aria-label", selected ? `${accessibleLabel}, selected` : accessibleLabel);
673
+ element.setAttribute("aria-roledescription", componentKind ?? node.kind);
674
+ if (element instanceof HTMLButtonElement) {
675
+ element.type = "button";
676
+ }
677
+ else {
678
+ element.setAttribute("role", "button");
679
+ element.tabIndex = 0;
680
+ }
681
+ element.style.left = `${node.rect.x}px`;
682
+ element.style.top = `${node.rect.y}px`;
683
+ element.style.width = `${Math.max(node.rect.width, 40)}px`;
684
+ element.style.minHeight = `${Math.max(node.rect.height, node.kind === "connector" ? 2 : componentKind === "badge" ? 28 : 40)}px`;
685
+ applyDeclaredStageStyles(element, node.style);
686
+ if (text.includes("\n") && element.style.whiteSpace.length === 0) {
687
+ element.style.whiteSpace = "pre-line";
688
+ }
689
+ const icons = readStageIcons(node);
690
+ if (componentKind === "button" || componentKind === "badge" || componentKind === "tabs") {
691
+ appendStageButtonLikeContent(element, text || node.name, icons, componentKind);
692
+ return element;
693
+ }
694
+ if (componentKind === "card" || componentKind === "dialog" || componentKind === "motion") {
695
+ appendStageCardContent(element, text || node.name, icons);
696
+ return element;
697
+ }
698
+ if (media) {
699
+ appendStageMediaContent(element, media, text || node.name);
700
+ return element;
701
+ }
702
+ if (node.kind === "connector") {
703
+ return element;
704
+ }
705
+ appendStageTextContent(element, text || node.name);
706
+ return element;
707
+ }
708
+ function describeStageNode(node, componentKind, media, text) {
709
+ const parts = [
710
+ node.name.trim(),
711
+ text.trim() && text.trim() !== node.name.trim() ? text.trim() : "",
712
+ media ? media.kind : "",
713
+ componentKind ?? node.kind
714
+ ].filter((value) => value.length > 0);
715
+ return parts.join(" • ");
716
+ }
717
+ function resolveStageBinding(documentState, node) {
718
+ const bindingId = typeof node.bindingRefs.primary === "string" ? node.bindingRefs.primary : null;
719
+ if (!bindingId) {
720
+ return null;
721
+ }
722
+ const binding = documentState.bindings.find((entry) => entry.id === bindingId);
723
+ if (!binding) {
724
+ return null;
725
+ }
726
+ const metadata = isRecord(binding.metadata) ? binding.metadata : {};
727
+ return {
728
+ componentName: typeof binding.componentName === "string" ? binding.componentName : null,
729
+ sourceKind: typeof metadata.sourceKind === "string" ? metadata.sourceKind : null
730
+ };
731
+ }
732
+ function resolveStageComponentKind(node, binding) {
733
+ const componentName = binding?.componentName?.toLowerCase() ?? "";
734
+ if (componentName.includes("button")) {
735
+ return "button";
736
+ }
737
+ if (componentName.includes("badge")) {
738
+ return "badge";
739
+ }
740
+ if (componentName.includes("tabs")) {
741
+ return "tabs";
742
+ }
743
+ if (componentName.includes("card")) {
744
+ return "card";
745
+ }
746
+ if (componentName.includes("dialog")) {
747
+ return "dialog";
748
+ }
749
+ if (componentName.includes("motion")) {
750
+ return "motion";
751
+ }
752
+ if (node.kind !== "component-instance") {
753
+ return null;
754
+ }
755
+ const lineCount = nodeText(node).split("\n").filter((entry) => entry.trim().length > 0).length;
756
+ if (lineCount > 1 || node.rect.height >= 96) {
757
+ return "card";
758
+ }
759
+ if (node.rect.height <= 56) {
760
+ return "badge";
761
+ }
762
+ return "button";
763
+ }
764
+ function readStageIcons(node) {
765
+ const refs = Array.isArray(node.metadata.iconRefs)
766
+ ? node.metadata.iconRefs
767
+ : node.metadata.iconRef
768
+ ? [node.metadata.iconRef]
769
+ : [];
770
+ return refs.flatMap((entry) => {
771
+ if (!isRecord(entry) || typeof entry.sourceLibrary !== "string") {
772
+ return [];
773
+ }
774
+ return [{
775
+ identifier: typeof entry.identifier === "string" ? entry.identifier : "generic",
776
+ sourceLibrary: entry.sourceLibrary
777
+ }];
778
+ });
779
+ }
780
+ function readStageAttributes(node) {
781
+ return isRecord(node.props.attributes) ? node.props.attributes : {};
782
+ }
783
+ function resolveStageTagName(node) {
784
+ if (typeof node.props.tagName === "string" && node.props.tagName.trim().length > 0) {
785
+ return node.props.tagName.trim().toLowerCase();
786
+ }
787
+ const codeSync = isRecord(node.metadata.codeSync) ? node.metadata.codeSync : null;
788
+ if (codeSync && typeof codeSync.tagName === "string" && codeSync.tagName.trim().length > 0) {
789
+ return codeSync.tagName.trim().toLowerCase();
790
+ }
791
+ return null;
792
+ }
793
+ function resolveStageMediaDescriptor(documentState, node) {
794
+ const tagName = resolveStageTagName(node);
795
+ const attributes = readStageAttributes(node);
796
+ const assetIds = Array.isArray(node.metadata.assetIds)
797
+ ? node.metadata.assetIds.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
798
+ : [];
799
+ const asset = assetIds.length > 0
800
+ ? documentState.assets.find((entry) => entry.id === assetIds[0])
801
+ : null;
802
+ const assetKind = typeof asset?.kind === "string" ? asset.kind.toLowerCase() : null;
803
+ const assetMime = typeof asset?.mime === "string" ? asset.mime.toLowerCase() : null;
804
+ const src = typeof node.props.src === "string"
805
+ ? node.props.src
806
+ : typeof attributes.src === "string"
807
+ ? attributes.src
808
+ : typeof asset?.url === "string"
809
+ ? asset.url
810
+ : typeof asset?.repoPath === "string"
811
+ ? asset.repoPath
812
+ : null;
813
+ const poster = typeof node.props.poster === "string"
814
+ ? node.props.poster
815
+ : typeof attributes.poster === "string"
816
+ ? attributes.poster
817
+ : null;
818
+ const alt = typeof node.props.alt === "string"
819
+ ? node.props.alt
820
+ : typeof attributes.alt === "string"
821
+ ? attributes.alt
822
+ : node.name;
823
+ if (tagName === "img" || assetKind === "image" || assetMime?.startsWith("image/")) {
824
+ return { kind: "image", src, poster: null, alt };
825
+ }
826
+ if (tagName === "video" || assetKind === "video" || assetMime?.startsWith("video/")) {
827
+ return { kind: "video", src, poster, alt };
828
+ }
829
+ if (tagName === "audio" || assetKind === "audio" || assetMime?.startsWith("audio/")) {
830
+ return { kind: "audio", src, poster: null, alt };
831
+ }
832
+ return null;
833
+ }
834
+ function appendStageMediaContent(element, media, label) {
835
+ element.classList.add("canvas-node-media-shell");
836
+ if (!media.src) {
837
+ const placeholder = document.createElement("div");
838
+ placeholder.className = "canvas-node-media-placeholder";
839
+ placeholder.textContent = `${media.kind} source missing`;
840
+ element.append(placeholder);
841
+ return;
842
+ }
843
+ if (media.kind === "image") {
844
+ const image = document.createElement("img");
845
+ image.className = "canvas-node-media canvas-node-media-image";
846
+ image.src = media.src;
847
+ image.alt = media.alt ?? label;
848
+ image.loading = "lazy";
849
+ image.draggable = false;
850
+ image.style.pointerEvents = "none";
851
+ element.append(image);
852
+ return;
853
+ }
854
+ if (media.kind === "video") {
855
+ const video = document.createElement("video");
856
+ video.className = "canvas-node-media canvas-node-media-video";
857
+ video.src = media.src;
858
+ if (media.poster) {
859
+ video.poster = media.poster;
860
+ }
861
+ video.muted = true;
862
+ video.loop = true;
863
+ video.autoplay = true;
864
+ video.playsInline = true;
865
+ video.preload = "metadata";
866
+ video.style.pointerEvents = "none";
867
+ element.append(video);
868
+ return;
869
+ }
870
+ const audio = document.createElement("audio");
871
+ audio.className = "canvas-node-media canvas-node-media-audio";
872
+ audio.src = media.src;
873
+ audio.controls = true;
874
+ audio.preload = "metadata";
875
+ audio.style.pointerEvents = "none";
876
+ element.append(audio);
877
+ }
878
+ function appendStageButtonLikeContent(element, text, icons, componentKind) {
879
+ const row = document.createElement(componentKind === "tabs" ? "div" : "span");
880
+ row.className = componentKind === "tabs" ? "canvas-node-tabs-trigger" : "canvas-node-row";
881
+ for (const icon of icons) {
882
+ row.append(buildStageIconElement(icon));
883
+ }
884
+ const label = document.createElement("span");
885
+ label.className = "canvas-node-label";
886
+ label.textContent = text;
887
+ row.append(label);
888
+ element.append(row);
889
+ }
890
+ function appendStageCardContent(element, text, icons) {
891
+ const lines = text.split("\n").map((entry) => entry.trim()).filter(Boolean);
892
+ const header = document.createElement("div");
893
+ header.className = "canvas-node-card-header";
894
+ const title = document.createElement("div");
895
+ title.className = "canvas-node-card-title";
896
+ title.textContent = lines[0] ?? text;
897
+ header.append(title);
898
+ if (icons.length > 0) {
899
+ const iconWrap = document.createElement("span");
900
+ iconWrap.className = "canvas-node-icon-stack";
901
+ for (const icon of icons) {
902
+ iconWrap.append(buildStageIconElement(icon));
903
+ }
904
+ header.append(iconWrap);
905
+ }
906
+ element.append(header);
907
+ if (lines.length > 1) {
908
+ const body = document.createElement("div");
909
+ body.className = "canvas-node-card-copy";
910
+ for (const line of lines.slice(1)) {
911
+ const paragraph = document.createElement("p");
912
+ paragraph.textContent = line;
913
+ body.append(paragraph);
914
+ }
915
+ element.append(body);
916
+ }
917
+ }
918
+ function appendStageTextContent(element, text) {
919
+ const inlineItems = text.split(/\s{2,}/).map((entry) => entry.trim()).filter(Boolean);
920
+ if (inlineItems.length > 1) {
921
+ const list = document.createElement("div");
922
+ list.className = "canvas-node-inline-list";
923
+ for (const item of inlineItems) {
924
+ const chip = document.createElement("span");
925
+ chip.className = "canvas-node-inline-item";
926
+ chip.textContent = item;
927
+ list.append(chip);
928
+ }
929
+ element.append(list);
930
+ return;
931
+ }
932
+ element.textContent = text;
933
+ }
934
+ function buildStageIconElement(icon) {
935
+ const span = document.createElement("span");
936
+ span.className = "canvas-node-icon";
937
+ span.dataset.library = icon.sourceLibrary;
938
+ if (icon.sourceLibrary === "tabler") {
939
+ span.append(buildStageTablerIcon(icon.identifier));
940
+ return span;
941
+ }
942
+ if (icon.sourceLibrary === "microsoft-fluent-ui-system-icons") {
943
+ span.append(buildStageFluentIcon(icon.identifier));
944
+ return span;
945
+ }
946
+ if (icon.sourceLibrary === "3dicons") {
947
+ span.style.background = "linear-gradient(145deg, #7ef9e9 0%, #22c3ee 48%, #ff7aa2 100%)";
948
+ span.style.boxShadow = "0 8px 18px rgba(34, 195, 238, 0.28)";
949
+ span.textContent = "";
950
+ return span;
951
+ }
952
+ span.textContent = icon.identifier.includes("party") ? "🎉" : "✨";
953
+ return span;
954
+ }
955
+ function buildStageIconSvg() {
956
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
957
+ svg.setAttribute("viewBox", "0 0 24 24");
958
+ svg.setAttribute("fill", "none");
959
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
960
+ svg.setAttribute("aria-hidden", "true");
961
+ return svg;
962
+ }
963
+ function appendStageStrokePath(svg, attributes) {
964
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
965
+ path.setAttribute("stroke", "currentColor");
966
+ path.setAttribute("stroke-linecap", "round");
967
+ path.setAttribute("stroke-linejoin", "round");
968
+ path.setAttribute("stroke-width", "1.85");
969
+ for (const [key, value] of Object.entries(attributes)) {
970
+ path.setAttribute(key, value);
971
+ }
972
+ svg.append(path);
973
+ }
974
+ function buildStageTablerIcon(identifier) {
975
+ const svg = buildStageIconSvg();
976
+ switch (identifier) {
977
+ case "arrow-right":
978
+ appendStageStrokePath(svg, { d: "M5 12h14" });
979
+ appendStageStrokePath(svg, { d: "m12 5 7 7-7 7" });
980
+ return svg;
981
+ case "rocket":
982
+ appendStageStrokePath(svg, { d: "M5 19c2.5-6.5 7.5-11.5 14-14-2.5 6.5-7.5 11.5-14 14Z" });
983
+ appendStageStrokePath(svg, { d: "m9 15-4 4" });
984
+ {
985
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
986
+ circle.setAttribute("cx", "14.5");
987
+ circle.setAttribute("cy", "9.5");
988
+ circle.setAttribute("r", "1.75");
989
+ circle.setAttribute("stroke", "currentColor");
990
+ circle.setAttribute("stroke-width", "1.85");
991
+ svg.append(circle);
992
+ }
993
+ return svg;
994
+ case "components":
995
+ case "layout-dashboard": {
996
+ const shapes = identifier === "components"
997
+ ? [
998
+ ["4", "4", "7", "7"],
999
+ ["13", "4", "7", "7"],
1000
+ ["8.5", "13", "7", "7"]
1001
+ ]
1002
+ : [
1003
+ ["4", "4", "7", "7"],
1004
+ ["13", "4", "7", "5"],
1005
+ ["13", "11", "7", "9"],
1006
+ ["4", "13", "7", "7"]
1007
+ ];
1008
+ for (const [x, y, width, height] of shapes) {
1009
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
1010
+ rect.setAttribute("x", x);
1011
+ rect.setAttribute("y", y);
1012
+ rect.setAttribute("width", width);
1013
+ rect.setAttribute("height", height);
1014
+ rect.setAttribute("rx", "2");
1015
+ rect.setAttribute("stroke", "currentColor");
1016
+ rect.setAttribute("stroke-width", "1.85");
1017
+ svg.append(rect);
1018
+ }
1019
+ return svg;
1020
+ }
1021
+ default:
1022
+ appendStageStrokePath(svg, { d: "M12 9v6" });
1023
+ appendStageStrokePath(svg, { d: "M9 12h6" });
1024
+ {
1025
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1026
+ circle.setAttribute("cx", "12");
1027
+ circle.setAttribute("cy", "12");
1028
+ circle.setAttribute("r", "7");
1029
+ circle.setAttribute("stroke", "currentColor");
1030
+ circle.setAttribute("stroke-width", "1.85");
1031
+ svg.append(circle);
1032
+ }
1033
+ return svg;
1034
+ }
1035
+ }
1036
+ function buildStageFluentIcon(identifier) {
1037
+ const svg = buildStageIconSvg();
1038
+ switch (identifier) {
1039
+ case "grid-dots-24":
1040
+ for (let index = 0; index < 9; index += 1) {
1041
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1042
+ circle.setAttribute("cx", String(7 + (index % 3) * 5));
1043
+ circle.setAttribute("cy", String(7 + Math.floor(index / 3) * 5));
1044
+ circle.setAttribute("r", "1.4");
1045
+ circle.setAttribute("fill", "currentColor");
1046
+ svg.append(circle);
1047
+ }
1048
+ return svg;
1049
+ case "chat-bubbles-24": {
1050
+ const bubble = document.createElementNS("http://www.w3.org/2000/svg", "path");
1051
+ bubble.setAttribute("d", "M5.5 8.5a3.5 3.5 0 0 1 3.5-3.5h8a3.5 3.5 0 0 1 3.5 3.5v4A3.5 3.5 0 0 1 17 16H11l-4.5 3v-3.1A3.49 3.49 0 0 1 5.5 12.5v-4Z");
1052
+ bubble.setAttribute("stroke", "currentColor");
1053
+ bubble.setAttribute("stroke-width", "1.8");
1054
+ svg.append(bubble);
1055
+ const lines = document.createElementNS("http://www.w3.org/2000/svg", "path");
1056
+ lines.setAttribute("d", "M8 9.75h7M8 12.75h4.5");
1057
+ lines.setAttribute("stroke", "currentColor");
1058
+ lines.setAttribute("stroke-linecap", "round");
1059
+ lines.setAttribute("stroke-width", "1.8");
1060
+ svg.append(lines);
1061
+ return svg;
1062
+ }
1063
+ case "branch-24":
1064
+ appendStageStrokePath(svg, { d: "M8 7.5v9" });
1065
+ appendStageStrokePath(svg, { d: "M8 12.5h7" });
1066
+ appendStageStrokePath(svg, { d: "M15 12.5V7.5" });
1067
+ for (const [cx, cy] of [["8", "6"], ["15", "6"], ["15", "18"]]) {
1068
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1069
+ circle.setAttribute("cx", cx);
1070
+ circle.setAttribute("cy", cy);
1071
+ circle.setAttribute("r", "2");
1072
+ circle.setAttribute("fill", "currentColor");
1073
+ svg.append(circle);
1074
+ }
1075
+ return svg;
1076
+ case "sparkle-24":
1077
+ default: {
1078
+ const sparkle = document.createElementNS("http://www.w3.org/2000/svg", "path");
1079
+ sparkle.setAttribute("d", "M12 3.5 14.4 9l5.6 2.4-5.6 2.4L12 19.5l-2.4-5.7L4 11.4 9.6 9 12 3.5Z");
1080
+ sparkle.setAttribute("fill", "currentColor");
1081
+ svg.append(sparkle);
1082
+ return svg;
1083
+ }
1084
+ }
1085
+ }
1086
+ function applyDeclaredStageStyles(element, style) {
1087
+ for (const [key, value] of Object.entries(style)) {
1088
+ if (typeof value !== "string" && typeof value !== "number") {
1089
+ continue;
1090
+ }
1091
+ const cssName = key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
1092
+ const cssValue = typeof value === "number" && !UNIT_LESS_STYLES.has(key) ? `${value}px` : String(value);
1093
+ element.style.setProperty(cssName, cssValue);
1094
+ }
1095
+ }
1096
+ function renderInspector() {
1097
+ const node = getSelectedNode();
1098
+ nameInput.disabled = !node || Boolean(currentState?.pendingMutation);
1099
+ textInput.disabled = !node || Boolean(currentState?.pendingMutation);
1100
+ deleteNodeButton.disabled = !node || Boolean(currentState?.pendingMutation);
1101
+ nameInput.value = node?.name ?? "";
1102
+ textInput.value = node ? nodeText(node) : "";
1103
+ renderSelectionMeta();
1104
+ }
1105
+ function renderSelectionMeta() {
1106
+ selectionMetaElement.innerHTML = "";
1107
+ const node = getSelectedNode();
1108
+ const items = [
1109
+ ["Selected", node?.id ?? "none"],
1110
+ ["Position", node ? `${node.rect.x}, ${node.rect.y}` : "n/a"],
1111
+ ["Size", node ? `${node.rect.width} × ${node.rect.height}` : "n/a"]
1112
+ ];
1113
+ for (const [label, value] of items) {
1114
+ const row = document.createElement("div");
1115
+ row.className = "canvas-summary-item";
1116
+ const title = document.createElement("div");
1117
+ title.className = "canvas-summary-label";
1118
+ title.textContent = label;
1119
+ const body = document.createElement("div");
1120
+ body.className = "canvas-summary-value";
1121
+ body.textContent = value;
1122
+ row.append(title, body);
1123
+ selectionMetaElement.append(row);
1124
+ }
1125
+ }
1126
+ function renderAnnotationPanel() {
1127
+ const node = getSelectedNode();
1128
+ annotationAddButton.disabled = !node || !currentState || annotationDrafts.some((entry) => entry.nodeId === node.id);
1129
+ annotationCopyButton.disabled = annotationDrafts.length === 0 || !currentState;
1130
+ annotationSendButton.disabled = annotationDrafts.length === 0 || !currentState;
1131
+ annotationListElement.innerHTML = "";
1132
+ if (!currentState || annotationDrafts.length === 0) {
1133
+ const empty = document.createElement("div");
1134
+ empty.className = "canvas-annotation-empty";
1135
+ empty.textContent = currentState ? "No canvas annotations captured yet." : "Waiting for canvas state...";
1136
+ annotationListElement.append(empty);
1137
+ return;
1138
+ }
1139
+ for (const draft of annotationDrafts) {
1140
+ const itemPayload = buildCanvasAnnotationPayloadForDrafts([draft]);
1141
+ const annotation = itemPayload?.annotations[0];
1142
+ const nodeLabel = annotation ? describeAnnotationItem(annotation) : draft.nodeId;
1143
+ const row = document.createElement("div");
1144
+ row.className = "canvas-annotation-item";
1145
+ const head = document.createElement("div");
1146
+ head.className = "canvas-annotation-head";
1147
+ const summary = document.createElement("div");
1148
+ const title = document.createElement("div");
1149
+ title.className = "canvas-annotation-title";
1150
+ title.textContent = nodeLabel;
1151
+ const meta = document.createElement("div");
1152
+ meta.className = "canvas-annotation-meta";
1153
+ meta.textContent = annotation ? `${annotation.tag} • ${Math.round(annotation.rect.width)}×${Math.round(annotation.rect.height)}` : draft.nodeId;
1154
+ summary.append(title, meta);
1155
+ const removeButton = document.createElement("button");
1156
+ removeButton.className = "canvas-button";
1157
+ removeButton.type = "button";
1158
+ removeButton.textContent = "Remove";
1159
+ removeButton.addEventListener("click", () => {
1160
+ removeAnnotationDraft(draft.nodeId);
1161
+ });
1162
+ head.append(summary, removeButton);
1163
+ const noteField = document.createElement("textarea");
1164
+ noteField.className = "canvas-textarea";
1165
+ noteField.value = draft.note ?? "";
1166
+ noteField.placeholder = "Add note for this node";
1167
+ noteField.addEventListener("input", () => {
1168
+ updateAnnotationDraft(draft.nodeId, { note: noteField.value });
1169
+ });
1170
+ const actions = document.createElement("div");
1171
+ actions.className = "canvas-actions-grid";
1172
+ const copyButton = document.createElement("button");
1173
+ copyButton.className = "canvas-button";
1174
+ copyButton.type = "button";
1175
+ copyButton.textContent = "Copy item";
1176
+ copyButton.addEventListener("click", () => {
1177
+ void copyCanvasAnnotation([draft], copyButton).catch((error) => {
1178
+ setCanvasButtonFeedback(copyButton, "Copy failed");
1179
+ console.error("[opendevbrowser canvas]", error);
1180
+ });
1181
+ });
1182
+ const sendButton = document.createElement("button");
1183
+ sendButton.className = "canvas-button";
1184
+ sendButton.type = "button";
1185
+ sendButton.textContent = "Send item";
1186
+ sendButton.addEventListener("click", () => {
1187
+ void sendCanvasAnnotation([draft], "canvas_item", nodeLabel, sendButton).catch((error) => {
1188
+ setCanvasButtonFeedback(sendButton, "Send failed");
1189
+ console.error("[opendevbrowser canvas]", error);
1190
+ });
1191
+ });
1192
+ actions.append(copyButton, sendButton);
1193
+ row.append(head, noteField, actions);
1194
+ annotationListElement.append(row);
1195
+ }
1196
+ }
1197
+ function buildCanvasAnnotationPayloadForDrafts(drafts) {
1198
+ if (!currentState) {
1199
+ return null;
1200
+ }
1201
+ const page = currentState.document.pages[0];
1202
+ if (!page || drafts.length === 0) {
1203
+ return null;
1204
+ }
1205
+ return buildCanvasAnnotationPayload({
1206
+ document: currentState.document,
1207
+ page,
1208
+ drafts,
1209
+ context: annotationContextInput.value.trim() || undefined
1210
+ });
1211
+ }
1212
+ function addSelectedAnnotationDraft() {
1213
+ const node = getSelectedNode();
1214
+ if (!node || annotationDrafts.some((entry) => entry.nodeId === node.id)) {
1215
+ renderAnnotationPanel();
1216
+ return;
1217
+ }
1218
+ annotationDrafts = [...annotationDrafts, { nodeId: node.id, note: "" }];
1219
+ renderAnnotationPanel();
1220
+ }
1221
+ function updateAnnotationDraft(nodeId, patch) {
1222
+ annotationDrafts = annotationDrafts.map((entry) => entry.nodeId === nodeId ? { ...entry, ...patch } : entry);
1223
+ }
1224
+ function removeAnnotationDraft(nodeId) {
1225
+ annotationDrafts = annotationDrafts.filter((entry) => entry.nodeId !== nodeId);
1226
+ renderAnnotationPanel();
1227
+ }
1228
+ function syncAnnotationDrafts() {
1229
+ if (!currentState) {
1230
+ annotationDrafts = [];
1231
+ renderAnnotationPanel();
1232
+ return;
1233
+ }
1234
+ const page = currentState.document.pages[0];
1235
+ const validIds = new Set(page?.nodes.map((node) => node.id) ?? []);
1236
+ const next = annotationDrafts.filter((entry) => validIds.has(entry.nodeId));
1237
+ if (next.length !== annotationDrafts.length) {
1238
+ annotationDrafts = next;
1239
+ }
1240
+ renderAnnotationPanel();
1241
+ }
1242
+ async function copyCanvasAnnotation(drafts, button) {
1243
+ const payload = buildCanvasAnnotationPayloadForDrafts(drafts ?? annotationDrafts);
1244
+ if (!payload) {
1245
+ setCanvasButtonFeedback(button, "No items");
1246
+ return;
1247
+ }
1248
+ await writeTextToClipboard(JSON.stringify(payload, null, 2));
1249
+ setCanvasButtonFeedback(button, "Copied");
1250
+ }
1251
+ async function sendCanvasAnnotation(drafts, source, label, button) {
1252
+ const payload = buildCanvasAnnotationPayloadForDrafts(drafts ?? annotationDrafts);
1253
+ if (!payload) {
1254
+ setCanvasButtonFeedback(button, "No items");
1255
+ return;
1256
+ }
1257
+ const response = await new Promise((resolve, reject) => {
1258
+ chrome.runtime.sendMessage({
1259
+ type: "annotation:sendPayload",
1260
+ payload,
1261
+ source,
1262
+ label
1263
+ }, (message) => {
1264
+ const lastError = chrome.runtime.lastError;
1265
+ if (lastError) {
1266
+ reject(new Error(lastError.message));
1267
+ return;
1268
+ }
1269
+ resolve(message);
1270
+ });
1271
+ });
1272
+ if (!response.ok) {
1273
+ throw new Error(response.error?.message ?? "Canvas annotation send failed.");
1274
+ }
1275
+ setCanvasButtonFeedback(button, "Sent");
1276
+ }
1277
+ async function writeTextToClipboard(text) {
1278
+ if (navigator.clipboard?.writeText) {
1279
+ try {
1280
+ await navigator.clipboard.writeText(text);
1281
+ return;
1282
+ }
1283
+ catch {
1284
+ // Fall back to execCommand below.
1285
+ }
1286
+ }
1287
+ const textarea = document.createElement("textarea");
1288
+ textarea.value = text;
1289
+ textarea.setAttribute("readonly", "true");
1290
+ textarea.style.position = "fixed";
1291
+ textarea.style.left = "-9999px";
1292
+ document.body.append(textarea);
1293
+ textarea.select();
1294
+ const ok = document.execCommand("copy");
1295
+ textarea.remove();
1296
+ if (!ok) {
1297
+ throw new Error("Clipboard copy failed");
1298
+ }
1299
+ }
1300
+ function setCanvasButtonFeedback(button, label) {
1301
+ const original = button.dataset.originalLabel ?? button.textContent ?? "Action";
1302
+ if (!button.dataset.originalLabel) {
1303
+ button.dataset.originalLabel = original;
1304
+ }
1305
+ button.textContent = label;
1306
+ window.setTimeout(() => {
1307
+ button.textContent = original;
1308
+ }, 1500);
1309
+ }
1310
+ function getSelectedNode() {
1311
+ if (!currentState?.selection.nodeId) {
1312
+ return null;
1313
+ }
1314
+ return findNode(currentState.document, currentState.selection.nodeId);
1315
+ }
1316
+ function applyOptimisticPatch(patches, selection, options = {}) {
1317
+ if (!currentState || currentState.documentRevision === null || currentState.pendingMutation) {
1318
+ return;
1319
+ }
1320
+ if (!options.skipOptimisticMutation) {
1321
+ applyLocalPatchMutation(currentState.document, patches);
1322
+ }
1323
+ currentState.pendingMutation = true;
1324
+ currentState.selection = {
1325
+ pageId: selection?.pageId ?? currentState.selection.pageId,
1326
+ nodeId: selection?.nodeId ?? currentState.selection.nodeId,
1327
+ targetId: selection?.targetId ?? currentState.selection.targetId,
1328
+ updatedAt: new Date().toISOString()
1329
+ };
1330
+ renderState();
1331
+ schedulePersist(currentState);
1332
+ port.postMessage({
1333
+ type: "canvas-page-patch-request",
1334
+ baseRevision: currentState.documentRevision,
1335
+ patches,
1336
+ selection: currentState.selection
1337
+ });
1338
+ }
1339
+ function applyLocalPatchMutation(document, patches) {
1340
+ for (const patch of patches) {
1341
+ if (!isRecord(patch) || typeof patch.op !== "string") {
1342
+ continue;
1343
+ }
1344
+ if (patch.op === "node.insert") {
1345
+ const page = document.pages.find((entry) => entry.id === patch.pageId);
1346
+ const node = isRecord(patch.node) ? patch.node : null;
1347
+ if (!page || !node || typeof node.id !== "string" || typeof node.kind !== "string") {
1348
+ continue;
1349
+ }
1350
+ const inserted = {
1351
+ id: node.id,
1352
+ kind: node.kind,
1353
+ name: typeof node.name === "string" ? node.name : node.id,
1354
+ pageId: typeof patch.pageId === "string" ? patch.pageId : page.id,
1355
+ parentId: typeof patch.parentId === "string" ? patch.parentId : null,
1356
+ childIds: Array.isArray(node.childIds) ? node.childIds.filter((entry) => typeof entry === "string") : [],
1357
+ rect: isRecord(node.rect) ? {
1358
+ x: typeof node.rect.x === "number" ? node.rect.x : 0,
1359
+ y: typeof node.rect.y === "number" ? node.rect.y : 0,
1360
+ width: typeof node.rect.width === "number" ? node.rect.width : 240,
1361
+ height: typeof node.rect.height === "number" ? node.rect.height : 120
1362
+ } : { x: 0, y: 0, width: 240, height: 120 },
1363
+ props: isRecord(node.props) ? { ...node.props } : {},
1364
+ style: isRecord(node.style) ? { ...node.style } : {},
1365
+ bindingRefs: isRecord(node.bindingRefs) ? { ...node.bindingRefs } : {},
1366
+ metadata: isRecord(node.metadata) ? { ...node.metadata } : {}
1367
+ };
1368
+ page.nodes.push(inserted);
1369
+ if (inserted.parentId) {
1370
+ const parent = page.nodes.find((entry) => entry.id === inserted.parentId);
1371
+ if (parent && !parent.childIds.includes(inserted.id)) {
1372
+ parent.childIds.push(inserted.id);
1373
+ }
1374
+ }
1375
+ else {
1376
+ page.rootNodeId = inserted.id;
1377
+ }
1378
+ continue;
1379
+ }
1380
+ if (patch.op === "node.remove" && typeof patch.nodeId === "string") {
1381
+ for (const page of document.pages) {
1382
+ page.nodes = page.nodes.filter((entry) => entry.id !== patch.nodeId);
1383
+ for (const node of page.nodes) {
1384
+ node.childIds = node.childIds.filter((entry) => entry !== patch.nodeId);
1385
+ }
1386
+ if (page.rootNodeId === patch.nodeId) {
1387
+ page.rootNodeId = null;
1388
+ }
1389
+ }
1390
+ continue;
1391
+ }
1392
+ if (patch.op === "node.update" && typeof patch.nodeId === "string" && isRecord(patch.changes)) {
1393
+ const node = findNode(document, patch.nodeId);
1394
+ if (!node) {
1395
+ continue;
1396
+ }
1397
+ for (const [path, value] of Object.entries(patch.changes)) {
1398
+ setNestedValue(node, path, value);
1399
+ }
1400
+ }
1401
+ }
1402
+ }
1403
+ function postViewState() {
1404
+ if (!currentState) {
1405
+ return;
1406
+ }
1407
+ port.postMessage({
1408
+ type: "canvas-page-view-state",
1409
+ viewport: currentState.viewport,
1410
+ selection: currentState.selection
1411
+ });
1412
+ }
1413
+ function schedulePersist(state) {
1414
+ if (persistTimer !== null) {
1415
+ window.clearTimeout(persistTimer);
1416
+ }
1417
+ persistTimer = window.setTimeout(() => {
1418
+ void flushPersist(state);
1419
+ }, SAVE_DEBOUNCE_MS);
1420
+ }
1421
+ async function flushPersist(state = currentState) {
1422
+ if (!state) {
1423
+ return;
1424
+ }
1425
+ if (persistTimer !== null) {
1426
+ window.clearTimeout(persistTimer);
1427
+ persistTimer = null;
1428
+ }
1429
+ await saveCachedState(currentTabId, state);
1430
+ broadcastChannel?.postMessage({ type: "canvas-page:broadcast", state });
1431
+ }
1432
+ async function getCurrentTabId() {
1433
+ return await new Promise((resolve) => {
1434
+ chrome.tabs.getCurrent((tab) => {
1435
+ const tabId = tab?.id;
1436
+ resolve(typeof tabId === "number" ? tabId : null);
1437
+ });
1438
+ });
1439
+ }
1440
+ async function openDatabase() {
1441
+ if (databasePromise) {
1442
+ return await databasePromise;
1443
+ }
1444
+ if (typeof indexedDB === "undefined") {
1445
+ return null;
1446
+ }
1447
+ databasePromise = new Promise((resolve) => {
1448
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
1449
+ request.onupgradeneeded = () => {
1450
+ const db = request.result;
1451
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
1452
+ db.createObjectStore(STORE_NAME);
1453
+ }
1454
+ };
1455
+ request.onsuccess = () => resolve(request.result);
1456
+ request.onerror = () => resolve(null);
1457
+ });
1458
+ return await databasePromise;
1459
+ }
1460
+ async function loadCachedState(tabId) {
1461
+ const db = await openDatabase();
1462
+ if (!db || tabId === null) {
1463
+ return null;
1464
+ }
1465
+ const tabState = await readStoredState(db, `tab:${tabId}`);
1466
+ if (tabState) {
1467
+ return tabState;
1468
+ }
1469
+ return null;
1470
+ }
1471
+ async function readStoredState(db, key) {
1472
+ return await new Promise((resolve) => {
1473
+ const transaction = db.transaction(STORE_NAME, "readonly");
1474
+ const request = transaction.objectStore(STORE_NAME).get(key);
1475
+ request.onsuccess = () => resolve(normalizeCanvasPageState(request.result));
1476
+ request.onerror = () => resolve(null);
1477
+ });
1478
+ }
1479
+ async function saveCachedState(tabId, state) {
1480
+ const db = await openDatabase();
1481
+ if (!db) {
1482
+ return;
1483
+ }
1484
+ const writes = [`doc:${state.documentId}`, `session:${state.canvasSessionId}`];
1485
+ if (tabId !== null) {
1486
+ writes.push(`tab:${tabId}`);
1487
+ }
1488
+ await Promise.all(writes.map((key) => writeStoredState(db, key, state)));
1489
+ }
1490
+ async function writeStoredState(db, key, state) {
1491
+ await new Promise((resolve) => {
1492
+ const transaction = db.transaction(STORE_NAME, "readwrite");
1493
+ const request = transaction.objectStore(STORE_NAME).put(state, key);
1494
+ request.onsuccess = () => resolve();
1495
+ request.onerror = () => resolve();
1496
+ });
1497
+ }
1498
+ function shouldAcceptBroadcast(state) {
1499
+ if (!currentState) {
1500
+ return true;
1501
+ }
1502
+ return state.documentId === currentState.documentId && state.updatedAt >= currentState.updatedAt;
1503
+ }
1504
+ function normalizeCanvasPageState(value) {
1505
+ if (!isRecord(value)) {
1506
+ return null;
1507
+ }
1508
+ if (typeof value.tabId !== "number"
1509
+ || typeof value.targetId !== "string"
1510
+ || typeof value.canvasSessionId !== "string"
1511
+ || typeof value.documentId !== "string"
1512
+ || typeof value.title !== "string"
1513
+ || typeof value.html !== "string"
1514
+ || typeof value.updatedAt !== "string"
1515
+ || !isRecord(value.document)) {
1516
+ return null;
1517
+ }
1518
+ const summary = normalizeCanvasSessionSummary(value.summary);
1519
+ return {
1520
+ tabId: value.tabId,
1521
+ targetId: value.targetId,
1522
+ canvasSessionId: value.canvasSessionId,
1523
+ documentId: value.documentId,
1524
+ documentRevision: typeof value.documentRevision === "number" ? value.documentRevision : null,
1525
+ title: value.title,
1526
+ document: normalizeDocument(value.document),
1527
+ html: value.html,
1528
+ previewMode: normalizePreviewState(value.previewMode) ?? "background",
1529
+ previewState: normalizePreviewState(value.previewState) ?? "background",
1530
+ updatedAt: value.updatedAt,
1531
+ summary,
1532
+ targets: normalizeCanvasTargetStateSummaries(value.targets ?? summary.targets),
1533
+ overlayMounts: Array.isArray(value.overlayMounts) ? value.overlayMounts.filter(isRecord) : [],
1534
+ feedback: Array.isArray(value.feedback) ? value.feedback.filter(isRecord) : [],
1535
+ feedbackCursor: typeof value.feedbackCursor === "string" ? value.feedbackCursor : null,
1536
+ selection: normalizeSelection(value.selection),
1537
+ viewport: normalizeViewport(value.viewport),
1538
+ pendingMutation: value.pendingMutation === true
1539
+ };
1540
+ }
1541
+ function normalizeDocument(value) {
1542
+ return {
1543
+ documentId: typeof value.documentId === "string" ? value.documentId : "dc_unknown",
1544
+ title: typeof value.title === "string" ? value.title : "OpenDevBrowser Canvas",
1545
+ updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : undefined,
1546
+ pages: Array.isArray(value.pages) ? value.pages.flatMap((entry) => normalizePage(entry)) : [],
1547
+ bindings: Array.isArray(value.bindings) ? value.bindings.flatMap((entry) => normalizeBinding(entry)) : [],
1548
+ assets: Array.isArray(value.assets) ? value.assets.flatMap((entry) => normalizeAsset(entry)) : [],
1549
+ componentInventory: Array.isArray(value.componentInventory)
1550
+ ? value.componentInventory.filter(isRecord)
1551
+ : []
1552
+ };
1553
+ }
1554
+ function normalizePage(value) {
1555
+ if (!isRecord(value) || typeof value.id !== "string") {
1556
+ return [];
1557
+ }
1558
+ const pageId = value.id;
1559
+ return [{
1560
+ id: pageId,
1561
+ name: typeof value.name === "string" ? value.name : pageId,
1562
+ path: typeof value.path === "string" ? value.path : "/",
1563
+ rootNodeId: typeof value.rootNodeId === "string" ? value.rootNodeId : null,
1564
+ prototypeIds: Array.isArray(value.prototypeIds) ? value.prototypeIds.filter((entry) => typeof entry === "string") : [],
1565
+ nodes: Array.isArray(value.nodes) ? value.nodes.flatMap((entry) => normalizeNode(entry, pageId)) : [],
1566
+ metadata: isRecord(value.metadata) ? value.metadata : {}
1567
+ }];
1568
+ }
1569
+ function normalizeNode(value, pageId) {
1570
+ if (!isRecord(value) || typeof value.id !== "string") {
1571
+ return [];
1572
+ }
1573
+ return [{
1574
+ id: value.id,
1575
+ kind: typeof value.kind === "string" ? value.kind : "frame",
1576
+ name: typeof value.name === "string" ? value.name : value.id,
1577
+ pageId,
1578
+ parentId: typeof value.parentId === "string" ? value.parentId : null,
1579
+ childIds: Array.isArray(value.childIds) ? value.childIds.filter((entry) => typeof entry === "string") : [],
1580
+ rect: isRecord(value.rect) ? {
1581
+ x: typeof value.rect.x === "number" ? value.rect.x : 0,
1582
+ y: typeof value.rect.y === "number" ? value.rect.y : 0,
1583
+ width: typeof value.rect.width === "number" ? value.rect.width : 240,
1584
+ height: typeof value.rect.height === "number" ? value.rect.height : 120
1585
+ } : { x: 0, y: 0, width: 240, height: 120 },
1586
+ props: isRecord(value.props) ? value.props : {},
1587
+ style: isRecord(value.style) ? value.style : {},
1588
+ bindingRefs: isRecord(value.bindingRefs) ? value.bindingRefs : {},
1589
+ metadata: isRecord(value.metadata) ? value.metadata : {}
1590
+ }];
1591
+ }
1592
+ function normalizeBinding(value) {
1593
+ if (!isRecord(value) || typeof value.id !== "string" || typeof value.nodeId !== "string") {
1594
+ return [];
1595
+ }
1596
+ return [{
1597
+ id: value.id,
1598
+ nodeId: value.nodeId,
1599
+ kind: typeof value.kind === "string" ? value.kind : "component",
1600
+ componentName: typeof value.componentName === "string" ? value.componentName : undefined,
1601
+ metadata: isRecord(value.metadata) ? value.metadata : {}
1602
+ }];
1603
+ }
1604
+ function normalizeAsset(value) {
1605
+ if (!isRecord(value) || typeof value.id !== "string") {
1606
+ return [];
1607
+ }
1608
+ return [{
1609
+ id: value.id,
1610
+ sourceType: typeof value.sourceType === "string" ? value.sourceType : undefined,
1611
+ kind: typeof value.kind === "string" ? value.kind : undefined,
1612
+ repoPath: typeof value.repoPath === "string" ? value.repoPath : null,
1613
+ url: typeof value.url === "string" ? value.url : null,
1614
+ mime: typeof value.mime === "string" ? value.mime : undefined,
1615
+ metadata: isRecord(value.metadata) ? value.metadata : {}
1616
+ }];
1617
+ }
1618
+ function normalizePreviewState(value) {
1619
+ return value === "focused" || value === "pinned" || value === "background" || value === "degraded"
1620
+ ? value
1621
+ : null;
1622
+ }
1623
+ function normalizeSelection(value) {
1624
+ if (!isRecord(value)) {
1625
+ return { pageId: null, nodeId: null, targetId: null, updatedAt: new Date().toISOString() };
1626
+ }
1627
+ return {
1628
+ pageId: typeof value.pageId === "string" || value.pageId === null ? value.pageId : null,
1629
+ nodeId: typeof value.nodeId === "string" || value.nodeId === null ? value.nodeId : null,
1630
+ targetId: typeof value.targetId === "string" || value.targetId === null ? value.targetId : null,
1631
+ updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : new Date().toISOString()
1632
+ };
1633
+ }
1634
+ function normalizeViewport(value) {
1635
+ if (!isRecord(value)) {
1636
+ return { ...DEFAULT_EDITOR_VIEWPORT };
1637
+ }
1638
+ return {
1639
+ x: typeof value.x === "number" ? value.x : DEFAULT_EDITOR_VIEWPORT.x,
1640
+ y: typeof value.y === "number" ? value.y : DEFAULT_EDITOR_VIEWPORT.y,
1641
+ zoom: typeof value.zoom === "number" ? value.zoom : DEFAULT_EDITOR_VIEWPORT.zoom
1642
+ };
1643
+ }
1644
+ function nodeText(node) {
1645
+ const raw = node.props.text ?? node.metadata.text;
1646
+ if (raw !== undefined && raw !== null) {
1647
+ return typeof raw === "string" ? raw : String(raw);
1648
+ }
1649
+ return node.kind === "text" || node.kind === "note" || node.kind === "component-instance"
1650
+ ? node.name
1651
+ : "";
1652
+ }
1653
+ function formatSummaryValue(value) {
1654
+ if (typeof value === "string" && value.trim().length > 0) {
1655
+ return value;
1656
+ }
1657
+ if (typeof value === "number" && Number.isFinite(value)) {
1658
+ return String(value);
1659
+ }
1660
+ return "n/a";
1661
+ }
1662
+ function formatAttachedClients(clients) {
1663
+ if (clients.length === 0) {
1664
+ return "none";
1665
+ }
1666
+ return clients
1667
+ .map((entry) => `${entry.clientId} (${entry.role === "lease_holder" ? "lease" : "observer"})`)
1668
+ .join(", ");
1669
+ }
1670
+ function formatCodeSyncStatus(summary, watchConflict) {
1671
+ if (!summary.codeSyncState && !summary.watchState && summary.bindings.length === 0) {
1672
+ return "not bound";
1673
+ }
1674
+ const segments = [
1675
+ summary.watchState ?? "idle",
1676
+ summary.codeSyncState ?? "idle",
1677
+ summary.driftState ?? "clean",
1678
+ watchConflict ? "watch conflict" : null
1679
+ ].filter((entry) => typeof entry === "string" && entry.length > 0);
1680
+ return segments.join(" • ");
1681
+ }
1682
+ function formatProjectionSummary(summary) {
1683
+ if (summary.activeProjections.length === 0) {
1684
+ return "n/a";
1685
+ }
1686
+ return summary.activeProjections.join(", ");
1687
+ }
1688
+ function readSummaryLibraryList(summary, key) {
1689
+ const libraryPolicy = isRecord(summary.libraryPolicy) ? summary.libraryPolicy : null;
1690
+ return libraryPolicy ? readSummaryStringArray(libraryPolicy[key]) : [];
1691
+ }
1692
+ function readSummaryStringArray(value) {
1693
+ return Array.isArray(value)
1694
+ ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
1695
+ : [];
1696
+ }
1697
+ function formatSummaryList(values) {
1698
+ return values.length > 0 ? values.join(", ") : "none";
1699
+ }
1700
+ function formatTimestamp(value) {
1701
+ const date = new Date(value);
1702
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
1703
+ }
1704
+ function formatViewport(viewport) {
1705
+ return `View ${viewport.zoom.toFixed(2)}× @ ${viewport.x}, ${viewport.y}`;
1706
+ }
1707
+ function countFeedbackItems(events) {
1708
+ return events.filter((entry) => entry.eventType === "feedback.item").length;
1709
+ }
1710
+ function computeDocumentBounds(nodes) {
1711
+ if (nodes.length === 0) {
1712
+ return { width: 1600, height: 1200 };
1713
+ }
1714
+ const maxX = Math.max(...nodes.map((node) => node.rect.x + node.rect.width));
1715
+ const maxY = Math.max(...nodes.map((node) => node.rect.y + node.rect.height));
1716
+ return {
1717
+ width: Math.max(maxX + 240, 1600),
1718
+ height: Math.max(maxY + 240, 1200)
1719
+ };
1720
+ }
1721
+ function queueViewportFitIfNeeded() {
1722
+ if (!currentState || !isDefaultEditorViewport(currentState.viewport)) {
1723
+ return;
1724
+ }
1725
+ const page = currentState.document.pages[0];
1726
+ if (!page || page.nodes.length === 0) {
1727
+ return;
1728
+ }
1729
+ if (fitViewportFrame !== null) {
1730
+ cancelAnimationFrame(fitViewportFrame);
1731
+ }
1732
+ fitViewportFrame = requestAnimationFrame(() => {
1733
+ fitViewportFrame = null;
1734
+ if (!currentState || !isDefaultEditorViewport(currentState.viewport)) {
1735
+ return;
1736
+ }
1737
+ const nextViewport = resolvePreferredViewport(currentState);
1738
+ if (nextViewport.x === currentState.viewport.x
1739
+ && nextViewport.y === currentState.viewport.y
1740
+ && nextViewport.zoom === currentState.viewport.zoom) {
1741
+ return;
1742
+ }
1743
+ currentState.viewport = nextViewport;
1744
+ toolbarMetaElement.textContent = formatViewport(currentState.viewport);
1745
+ renderStage();
1746
+ postViewState();
1747
+ schedulePersist(currentState);
1748
+ });
1749
+ }
1750
+ function resolvePreferredViewport(state) {
1751
+ const page = state.document.pages[0];
1752
+ if (!page || page.nodes.length === 0) {
1753
+ return { ...DEFAULT_EDITOR_VIEWPORT };
1754
+ }
1755
+ return computeFittedViewport(page.nodes, stageElement.clientWidth, stageElement.clientHeight);
1756
+ }
1757
+ function findNode(document, nodeId) {
1758
+ if (!document) {
1759
+ return null;
1760
+ }
1761
+ for (const page of document.pages) {
1762
+ const match = page.nodes.find((node) => node.id === nodeId);
1763
+ if (match) {
1764
+ return match;
1765
+ }
1766
+ }
1767
+ return null;
1768
+ }
1769
+ function setNestedValue(target, path, value) {
1770
+ const segments = path.split(".");
1771
+ let current = target;
1772
+ for (let index = 0; index < segments.length - 1; index += 1) {
1773
+ const segment = segments[index];
1774
+ if (!segment) {
1775
+ return;
1776
+ }
1777
+ const existing = current[segment];
1778
+ if (!isRecord(existing)) {
1779
+ current[segment] = {};
1780
+ }
1781
+ current = current[segment];
1782
+ }
1783
+ const last = segments[segments.length - 1];
1784
+ if (!last) {
1785
+ return;
1786
+ }
1787
+ current[last] = value;
1788
+ }
1789
+ function clamp(value, min, max) {
1790
+ return Math.min(Math.max(value, min), max);
1791
+ }
1792
+ function requiredElement(id) {
1793
+ const element = document.getElementById(id);
1794
+ if (!element) {
1795
+ throw new Error(`Missing element: ${id}`);
1796
+ }
1797
+ return element;
1798
+ }
1799
+ function isRecord(value) {
1800
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1801
+ }